我有一个音乐播放器应用,当点击某个项目时,它会将其状态更改为“播放”。状态,它展开以显示搜索栏,其他更改也发生,反之亦然。
问题是无论何时我向上或向下滚动,视图持有者都会在列表中交换其当前状态(在这种情况下播放,暂停可见性)和其他随机视图。值得一提的是,这在整个列表中是一致的,因此在每个特定数量的视图持有者之后,状态与被点击的状态相同(因此,如果在每10个视图之后,位置0处的视图处于播放可见性状态处于相同的状态,但请注意该歌曲仍然适用于正确的歌曲。)
这是适配器代码(从基本适配器扩展,可以在这里找到播放/暂停可见性的方法,这些方法在下面的歌曲片段代码中使用):
public class AllSongsAdapter extends BaseSongAdapter<AllSongsAdapter.AllSongsItemHolder> {
private ArrayList<Song> songList;
public AllSongsAdapter(){
super(null);
}
//OnCreateViewHolder was called for every view;
//FIX: return 0 for same type of views.
@Override
public int getItemViewType(int position) {
return 0;
}
//cursor moves to the appropriate position in the list so we just have to update our views
@Override
public void onBindViewHolder(AllSongsItemHolder holder, Cursor cursor) {
if(cursor!=null) {
int i = cursor.getPosition();
Log.d("on Bind", "i:" + i);
Song songItem = songList.get(i);
holder.songItemName.setText(songItem.title);
holder.songItemArtistName.setText(songItem.artistName);
holder.songItemAlbumName.setText(songItem.albumName);
}
}
@Override
public void swapCursor(Cursor newCursor) {
super.swapCursor(newCursor);
songList = SongsLoader.getSongsForCursor(newCursor);
}
@Override
public AllSongsItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.d("CREATE VIEW HOLDER", "holder" );
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.songs_item_list, parent, false);
return new AllSongsItemHolder(v);
}
private Uri getAlbumArtUri(long albumId) {
return ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"), albumId);
}
public void setPlayVisibility(View child, RecyclerView rv) {
//View v = rv.getChildAt(rv.getChildAdapterPosition(child));
AllSongsItemHolder holder = (AllSongsItemHolder) rv.getChildViewHolder(child);
// if(getItemId(position) == rv.getChildItemId(v)){}
holder.seekBar.setVisibility(View.VISIBLE);
holder.songItemTimer.setVisibility(View.VISIBLE);
holder.songItemImage.setScaleType(ImageView.ScaleType.CENTER);
holder.songItemImage.setBackgroundColor(Color.parseColor("#303F9F"));
}
public void setPauseVisibility(View child, RecyclerView rv) {
//View v = rv.getChildAt(rv.getChildAdapterPosition(child));
AllSongsItemHolder holder = (AllSongsItemHolder) rv.getChildViewHolder(child);
//if(getItemId(position) == rv.getChildItemId(v)){}
holder.seekBar.setVisibility(View.GONE);
holder.songItemTimer.setVisibility(View.GONE);
holder.songItemImage.setScaleType(ImageView.ScaleType.FIT_XY);
holder.songItemImage.setBackgroundColor(0);
}
static class AllSongsItemHolder extends RecyclerView.ViewHolder {
private ImageView songItemImage, songItemOptionDropDown;
private TextView songItemName, songItemAlbumName, songItemArtistName;
private View separator;
private SeekBar seekBar;
private ImageView nowPlayingIcon;
private TextView songItemTimer;
public AllSongsItemHolder(View v) {
super(v);
songItemImage = v.findViewById(R.id.songItemImage);
songItemOptionDropDown = v.findViewById(R.id.songItemOptionDropDown);
songItemAlbumName = v.findViewById(R.id.songItemAlbumName);
songItemArtistName = v.findViewById(R.id.songItemArtistName);
songItemName = v.findViewById(R.id.songItemName);
separator = v.findViewById(R.id.separator);
seekBar = v.findViewById(R.id.seekbar);
songItemTimer = v.findViewById(R.id.songItemTimer);
// nowPlayingIcon = v.findViewById(R.id.nowPlayingIcon);
}
}
}
我注意到当getItemViewType返回位置时,不会发生此问题,因为recycleler视图为列表中的每个项目保存一个实例。但显然这不是一个合适的解决方案,因为它在首次加载时会减慢滚动速度,因为它必须创建每个视图。可能与此有关吗?
BaseAdapter代码:
public abstract class BaseSongAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
protected Cursor mCursor;
protected int mRowIDColumn;
protected boolean mDataValid;
public BaseSongAdapter(Cursor c) {
init(c);
}
public BaseSongAdapter() {}
void init(Cursor c) {
boolean cursorPresent = c != null;
mCursor = c;
mDataValid = cursorPresent;
mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
setHasStableIds(true);
if(mDataValid && mCursor!=null) {
Log.d("VALID", "CURSOR");
}
}
@Override
public void onBindViewHolder(VH holder, int position) {
//Log.d("ON BIND","CALLED");
if (!mDataValid) {
throw new IllegalStateException("Cannot bind viewholder when cursor is in invalid state.");
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("Could not move cursor to position " + position + " when trying to bind viewholder");
}
onBindViewHolder(holder, mCursor);
}
public abstract void onBindViewHolder(VH holder, Cursor cursor);
@Override
public int getItemViewType(int position) {
return position;
}
@Override
public int getItemCount() {
if (mDataValid) {
return mCursor.getCount();
} else {
return 0;
}
}
@Override
public long getItemId(int position) {
if (!mDataValid) {
throw new IllegalStateException("Cannot lookup item id when cursor is in invalid state.");
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("Could not move cursor to position " + position + " when trying to get an item id");
}
return mCursor.getLong(mRowIDColumn);
}
public void swapCursor(Cursor newCursor) {
if (newCursor == mCursor) {
return;
}
//Log.d("TAG", "swapCursor");
//Cursor oldCursor = mCursor;
if (newCursor != null) {
mCursor = newCursor;
mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
mDataValid = true;
// notify the observers about the new cursor
notifyDataSetChanged();
} else {
mCursor = null;
mRowIDColumn = -1;
mDataValid = false;
// notify the observers about the lack of a data set
notifyItemRangeRemoved(0, getItemCount());
}
//return oldCursor;
}
}
处理点击的歌曲片段代码(在onActivityCreated中):
public class AllSongsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private final int LOADER_ID_ALLSONGS = 0;
private Context ctx;
private AllSongsAdapter mAdapter;
private MediaPlayerHolder mediaPlayerHolder;
//on click play,pause variables
private boolean playingCLicked = false;
private boolean firstTime = true;
private String playbackState;
private View lastChildView;
private long currentSongId;
private long newID;
private long nextID; //id for next song
private int lastTrackPosition; //for auto next track
private RecyclerView rv;
public AllSongsFragment() {
super();
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
ctx = context;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getLoaderManager().initLoader(LOADER_ID_ALLSONGS, null, this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.all_songs_fragment, container, false);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
rv = (RecyclerView) getActivity().findViewById(R.id.fragmentList);
mediaPlayerHolder = new MediaPlayerHolder(getActivity());
mediaPlayerHolder.setPlaybackInfoListener(new PlaybackListener());
rv.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), rv, new ClickListener() {
@Override
public void click(View view, final int position) {
Toast.makeText(getActivity(), "onClick " + position, Toast.LENGTH_SHORT).show();
newID = mAdapter.getItemId(position);
if (playingCLicked) {
if (mediaPlayerHolder.isPlaying()) {
if (currentSongId == newID) {
mAdapter.setPauseVisibility(view, rv);
lastChildView = view;
}
//PAUSE IT
mediaPlayerHolder.pause();
Log.d("playingclicked", "true" + "state" + playbackState);
//when a different song is clicked while current song is playing
if (playbackState.equalsIgnoreCase("paused") && newID != currentSongId) {
currentSongId = newID;
mAdapter.setPauseVisibility(lastChildView, rv);
mAdapter.setPlayVisibility(view, rv);
mediaPlayerHolder.reset();
mediaPlayerHolder.loadMedia(currentSongId);
mediaPlayerHolder.play();
lastTrackPosition = position;
lastChildView = view;
Log.d("else 1 positions", "currentsongID" + currentSongId + "lasttrack" + lastTrackPosition);
}
playingCLicked = !playingCLicked;
} else { //media is not playing
Log.d("else 1", "play");
if (firstTime) {
Log.d("different", "no");
currentSongId = newID;
lastTrackPosition = position;
lastChildView = view;
mediaPlayerHolder.reset();
mediaPlayerHolder.loadMedia(currentSongId);
Log.d("SelectedTrack", "" + mediaPlayerHolder.getMediaPlayer().getSelectedTrack(MEDIA_TRACK_TYPE_AUDIO));
firstTime = false;
}
if (newID != currentSongId) {
Log.d("different", "yes");
//mediaPlayerHolder.stop();
currentSongId = newID;
mediaPlayerHolder.reset();
mediaPlayerHolder.loadMedia(currentSongId);
firstTime = true;
}
if (currentSongId == newID)
mAdapter.setPlayVisibility(view, rv);
//PLAY IT
mediaPlayerHolder.play();
playingCLicked = !playingCLicked;
}
} else //----------playingclicked = false, first called--------------
{
if (!mediaPlayerHolder.isPlaying()) {
lastChildView = view;
mAdapter.setPlayVisibility(view, rv);
if (firstTime) {
Log.d("different", "no, first time");
currentSongId = newID;
lastTrackPosition = position;
lastChildView = view;
mediaPlayerHolder.reset();
mediaPlayerHolder.loadMedia(currentSongId);
Log.d("SelectedTrack", "" + mediaPlayerHolder.getMediaPlayer().getSelectedTrack(MEDIA_TRACK_TYPE_AUDIO));
firstTime = false;
}
Log.d("playbackState", playbackState + "currentId " + currentSongId);
//called when current song is paused and after playingClicked = true following is called if songId is different
if (newID != currentSongId) {
Log.d("different", "yes");
currentSongId = newID;
mediaPlayerHolder.stop();
mediaPlayerHolder.reset();
mediaPlayerHolder.loadMedia(currentSongId);
}
//PLAY IT
mediaPlayerHolder.play();
playingCLicked = !playingCLicked;
} else { //media is playing
Log.d("else 2", "pause");
if (newID == currentSongId) {
lastChildView = view;
lastTrackPosition = position;
mAdapter.setPauseVisibility(view, rv);
}
//PAUSE IT
mediaPlayerHolder.pause();
firstTime = false;
//when a different song is clicked while current song is playing
if (playbackState.equalsIgnoreCase("paused") && newID != currentSongId) {
currentSongId = newID;
mAdapter.setPauseVisibility(lastChildView, rv);
mAdapter.setPlayVisibility(view, rv);
mediaPlayerHolder.reset();
mediaPlayerHolder.loadMedia(currentSongId);
mediaPlayerHolder.play();
lastTrackPosition = position;
lastChildView = view;
Log.d("else 2 positions", "currentsongID" + currentSongId + "lasttrack" + lastTrackPosition);
}
playingCLicked = !playingCLicked;
}
}
}
@Override
public void onLongClick(View view, int position) {
Toast.makeText(getActivity(),"onLongClick " + position,Toast.LENGTH_SHORT).show();
}
}));
mAdapter = new AllSongsAdapter();
rv.setAdapter(mAdapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
rv.setLayoutManager(layoutManager);
//------------Temporary divider-----------------
RecyclerView.ItemDecoration itemDecoration = new DividerItemDecoration(getActivity(), layoutManager.getOrientation());
rv.addItemDecoration(itemDecoration);
}
@Override
public Loader onCreateLoader(int id, Bundle args) {
Log.d("CREATE LOADER", "SUCCESS");
Uri musicUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
final String[] PROJECTION = {"_id", "title", "artist", "album", "duration", "track", "artist_id", "album_id"};
return new CursorLoader(ctx,
musicUri,
PROJECTION,
null,
null,
MediaStore.Audio.Media.DEFAULT_SORT_ORDER );
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
Log.d("LOAD FINISHED", "SUCCESS");
}
@Override
public void onLoaderReset(Loader loader) {
Log.d("LOADER RESET", "CALLED");
mAdapter.swapCursor(null);
}
class PlaybackListener extends PlaybackStateListener{
@Override
void onDurationChanged(int duration) {
super.onDurationChanged(duration);
}
@Override
void onPositionChanged(int position) {
super.onPositionChanged(position);
}
@Override
void onStateChanged(int state) {
playbackState = PlaybackListener.convertStateToString(state);
}
@Override
void onPlaybackCompleted() {
//super.onPlaybackCompleted();
//pause visibility after completed
mAdapter.setPauseVisibility(lastChildView, rv);
}
@Override
void onLogUpdated(String formattedMessage) {
super.onLogUpdated(formattedMessage);
}
}
//---------------RECYCLER VIEW TOUCH EVENTS LISTENER CLASS------------------------
class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {
private GestureDetector gestureDetector;
private ClickListener mListener;
public RecyclerTouchListener(Context ctx, final RecyclerView recyclerView, ClickListener clickListener){
mListener = clickListener;
gestureDetector = new GestureDetector(ctx, new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
// Log.d("GESTURE DETECTED", "ACTION UP" + e);
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
//Log.d("GESTURE DETECTED", "LONG PRESS");
if(rv.getScrollState() == SCROLL_STATE_IDLE && !(rv.getScrollState() == SCROLL_STATE_DRAGGING)) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null && mListener != null) {
mListener.onLongClick(child, recyclerView.getChildLayoutPosition(child));
}
}
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
});
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
//Log.d("Intercept Event", "INTERCEPTING \n" + e);
if(rv.getScrollState() == SCROLL_STATE_IDLE && !(rv.getScrollState() == SCROLL_STATE_DRAGGING)) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && mListener != null && gestureDetector.onTouchEvent(e)) {
mListener.click(child, rv.getChildLayoutPosition(child));
}
}
//true if onTouchEvent is to be called, false if you want gesture detector to handle events
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
}
非常感谢任何帮助!!
答案 0 :(得分:0)
您需要在onBindViewHolder中设置播放/暂停/任何可见性,否则即使新模型对象绑定到该视图,它也将是最后一次设置。