Android使用RecyclerView實(shí)現(xiàn)今日頭條頻道管理功能
使用過今日頭條的伙計(jì)們對(duì)這個(gè)效果肯定很熟悉。拖拽可排序,點(diǎn)擊標(biāo)簽后可以刪除。今天我們采用RecyclerView來實(shí)現(xiàn)。
實(shí)現(xiàn)思路:
通過ItemTouchHelper來綁定RecyclerView的子控件觸摸事件。
當(dāng)滑動(dòng)拖拽的時(shí)候,通知適配器來交換兩個(gè)子控件的顯示位置。
更改數(shù)據(jù)源,使數(shù)據(jù)源與子空間顯示內(nèi)容一致。
這就是實(shí)現(xiàn)的基本思路,是不是很簡單?當(dāng)然,首先要了解一下ItemTouchHelper這哥們兒是干啥的,有什么作用。
This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.
It works with a RecyclerView and a Callback class, which configures what type of interactions are enabled and also receives events when user performs these actions.
Depending on which functionality you support, you should override onMove(RecyclerView, ViewHolder, ViewHolder) and / or onSwiped(ViewHolder, int).
This class is designed to work with any LayoutManager but for certain situations, it can be optimized for your custom LayoutManager by extending methods in the ItemTouchHelper.Callback class or implementing ItemTouchHelper.ViewDropHandler interface in your LayoutManager.
By default, ItemTouchHelper moves the items' translateX/Y properties to reposition them. You can customize these behaviors by overriding onChildDraw(Canvas, RecyclerView, ViewHolder, float, float, int, boolean) or onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int, boolean).
Most of the time you only need to override onChildDraw.
通過API文檔的介紹,這個(gè)哥們兒是為RecyclerView工作的,他需要一個(gè)CallBack,可以回調(diào)RecyclerView的子控件滑動(dòng)和拖拽事件,而且也可以通過這個(gè)CallBack重繪我們的子view。這就一目了然了嘛。通過使用ItemTouchHelper,可以很輕松的就實(shí)現(xiàn)了RecyclerView觸摸事件的回調(diào)。換句話說,只要我們?yōu)镽ecyclerView 綁定了ItemTouchHelper之后,RecyclerView子控件的拖動(dòng)和滑動(dòng)事件已經(jīng)幫我們實(shí)現(xiàn)了。我們所要做的就是在觸摸事件之后,如何去改變?nèi)ジ翧dapter和改變我們的數(shù)據(jù)。
開始擼碼:
public class ChannelActivity extends Activity { public RecyclerView rv; public List<DataBean> list = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_channel); initData(); initView(); } private void initView() { rv = (RecyclerView) findViewById(R.id.rl_view); rv.setLayoutManager(new GridLayoutManager(this, 4)); MyAdapter adapter = new MyAdapter(this, list); rv.setAdapter(adapter); //關(guān)聯(lián)ItemTouchHelper ItemTouchHelper touchHelper = new ItemTouchHelper(new MyItemTouchCallBack(adapter)); touchHelper.attachToRecyclerView(rv); } private void initData() { DataBean bean1 = new DataBean("體育", 0, "url"); DataBean bean2 = new DataBean("新聞", 1, "url"); DataBean bean3 = new DataBean("影視", 2, "url"); DataBean bean4 = new DataBean("電視劇", 3, "url"); DataBean bean5 = new DataBean("熱點(diǎn)", 4, "url"); DataBean bean6 = new DataBean("推薦", 5, "url"); DataBean bean7 = new DataBean("屌絲男士", 6, "url"); DataBean bean8 = new DataBean("音樂", 7, "url"); DataBean bean9 = new DataBean("電影", 8, "url"); list.add(bean1); list.add(bean2); list.add(bean3); list.add(bean4); list.add(bean5); list.add(bean6); list.add(bean7); list.add(bean8); list.add(bean9); } }
自定義MyItemTouchCallBack
public class MyItemTouchCallBack extends ItemTouchHelper.Callback { private TouchInterface touchInterface; public MyItemTouchCallBack(TouchInterface touchInterface) { this.touchInterface = touchInterface; } @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { //拖拽 int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT; //滑出屏幕 int swipeFlags = ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT | ItemTouchHelper.UP | ItemTouchHelper.DOWN; return makeMovementFlags(dragFlags, 0); } @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { int position_target = target.getLayoutPosition(); int position = viewHolder.getLayoutPosition(); //滑動(dòng)事件回調(diào)到了Adapter,用來處理數(shù)據(jù) touchInterface.onMove(position, position_target); return true; } //標(biāo)簽動(dòng)畫持續(xù)時(shí)間,默認(rèn)是250 @Override public long getAnimationDuration(RecyclerView recyclerView, int animationType, float animateDx, float animateDy) { return super.getAnimationDuration(recyclerView, animationType, animateDx, animateDy); } /** * 是否可以長按拖拽,默認(rèn)是true * * @return */ @Override public boolean isLongPressDragEnabled() { return super.isLongPressDragEnabled(); } /** * 標(biāo)簽劃出去的回調(diào),direction是滑動(dòng)的方向 * * @return */ @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { } }
Adapter類中處理數(shù)據(jù)
public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> implements TouchInterface { private Context context; //是否顯示delete public boolean isShow; public List<DataBean> getList() { return list; } public void setList(List<DataBean> list) { this.list = list; } private List<DataBean> list; public MyAdapter(Context context, List<DataBean> list) { this.context = context; this.list = list; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { MyViewHolder viewHolder = new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_layout, parent, false)); return viewHolder; } @Override public void onBindViewHolder(MyViewHolder holder, final int position) { holder.tv_des.setText(list.get(position).name); holder.tv_des.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { DataBean bean = list.remove(position); notifyDataSetChanged(); Toast.makeText(context,"刪除了"+bean.name+"頻道",Toast.LENGTH_SHORT).show(); } }); holder.tv_des.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { isShow = true; notifyDataSetChanged(); return true; } }); if (isShow) { holder.iv_icon.setVisibility(View.VISIBLE); } else { holder.iv_icon.setVisibility(View.GONE); } } @Override public int getItemCount() { return list.size(); } @Override public void onMove(int currentPosition, int targetPosition) { Collections.swap(list, currentPosition, targetPosition); if (targetPosition < currentPosition) { List<DataBean> subList = list.subList(targetPosition + 1, currentPosition + 1); //向右移一位 rightStepList(0, subList); } else { List<DataBean> subList = list.subList(currentPosition, targetPosition); //向左移一位 leftStepList(0, subList); } notifyItemMoved(currentPosition, targetPosition); } } class MyViewHolder extends RecyclerView.ViewHolder { public ImageView iv_icon; public TextView tv_des; public MyViewHolder(View itemView) { super(itemView); iv_icon = (ImageView) itemView.findViewById(R.id.iv_icon); tv_des = (TextView) itemView.findViewById(R.id.tv_des); } }
解釋一下onMove方法,例如:我們的數(shù)據(jù)是[1,2,3,4,5,6],當(dāng)6移動(dòng)到3的位置時(shí),那么數(shù)據(jù)源最后變化為[1,2,6,3,4,5]。但是在顯示的時(shí)候我們先是將當(dāng)前position和targetposition對(duì)調(diào)[1,2,6,4,5,3],然后取出[4,5,3]進(jìn)行右移一位,這樣數(shù)據(jù)源就對(duì)上了。如果是從3移動(dòng)到6進(jìn)行左移就可以了,數(shù)據(jù)排序的算法,采用反轉(zhuǎn)的思想。
public class DataUtils { /** * 利用反轉(zhuǎn)的思想對(duì)數(shù)據(jù)進(jìn)行排序 * 例如:list{0,1,2,3,4,5,6,7} 左移一位 * 第一步:第一位先反轉(zhuǎn){0,1,2,3,4,5,6,7} * 第二部:剩下的在反轉(zhuǎn){0,7,6,5,4,3,2,1} * 第三步:全部反轉(zhuǎn){1,2,3,4,5,6,7,0} * * 例如:list{0,1,2,3,4,5,6,7} 右移一位 * 第一步:最右邊一位先反轉(zhuǎn){1,2,3,4,5,6,7} * 第二部:剩下的在反轉(zhuǎn){6,5,4,3,2,1,0,7} * 第三步:全部反轉(zhuǎn){7,6,5,4,3,2,1,0} * * 因?yàn)閘ist的index是從0開始的,step要相應(yīng)的-1 * 優(yōu)點(diǎn):少創(chuàng)建對(duì)象,優(yōu)化內(nèi)存 * * @param start * @param end * @param list */ public static void reverseList(int start,int end,List list){ int count = (end+1-start)/2 ; for(int i = 0;i< count;i++){ Object temp = list.get(start+i); list.set(start+i,list.get(end-i)); list.set(end-i,temp); } } public static void leftStepList(int step,List list){ int size = list.size() -1; //左移 reverseList(0,step,list); reverseList(step+1,size,list); reverseList(0,size,list); } public static void rightStepList(int step,List list){ int size = list.size() -1; //右移 reverseList(size-step,size,list); reverseList(0,size-step-1,list); reverseList(0,size,list); } }
Activity布局文件:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_channel" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.iwintrue.channe.ChannelActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/rl_view" android:background="@color/white" android:layout_width="match_parent" android:layout_height="match_parent"></android.support.v7.widget.RecyclerView> </RelativeLayout>
子控件布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" > <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dp"> <TextView android:id="@+id/tv_des" android:layout_width="80dp" android:layout_height="30dp" android:text="屌絲男士" android:gravity="center" android:background="@drawable/rl_shape" android:textColor="@color/textColor" android:layout_marginTop="5dp" android:layout_marginRight="5dp" /> <ImageView android:layout_alignRight="@+id/tv_des" android:layout_marginRight="-5dp" android:id="@+id/iv_icon" android:layout_width="20dp" android:layout_height="20dp" android:src="@mipmap/delete" android:scaleType="fitXY" android:visibility="gone" /> </RelativeLayout> </LinearLayout>
實(shí)現(xiàn)效果:
github地址:https://github.com/zhoukai1526/Channel
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- android自定義view仿今日頭條加載文字變色效果
- Android仿今日頭條頂部導(dǎo)航欄效果的實(shí)例代碼
- Android仿今日頭條多個(gè)fragment懶加載的實(shí)現(xiàn)
- Android studio導(dǎo)入項(xiàng)目的方法詳解(簡單快速)
- Android 仿今日頭條簡單的刷新效果實(shí)例代碼
- Android仿今日頭條APP實(shí)現(xiàn)下拉導(dǎo)航選擇菜單效果
- Android應(yīng)用中仿今日頭條App制作ViewPager指示器
- Android實(shí)現(xiàn)仿網(wǎng)易今日頭條等自定義頻道listview 或者grideview等item上移到另一個(gè)view中
- Android仿今日頭條滑動(dòng)頁面導(dǎo)航效果
- Android實(shí)現(xiàn)今日頭條訂閱頻道效果
相關(guān)文章
Android編程實(shí)現(xiàn)禁止?fàn)顟B(tài)欄下拉的方法詳解
這篇文章主要介紹了Android編程實(shí)現(xiàn)禁止?fàn)顟B(tài)欄下拉的方法,結(jié)合實(shí)例形式詳細(xì)分析了Android狀態(tài)欄操作相關(guān)的函數(shù)、屬性調(diào)用及權(quán)限控制設(shè)置技巧,需要的朋友可以參考下2017-08-08Android 對(duì)話框(Dialog)大全示例(建立你自己的對(duì)話框)
android開發(fā)中,對(duì)話框的使用還是很平凡的,本篇文章介紹了Android 對(duì)話框的實(shí)例,詳細(xì)的介紹了多種對(duì)話框的方法,有興趣的可以了解一下。2016-11-11android實(shí)現(xiàn)自動(dòng)發(fā)送郵件
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)自動(dòng)發(fā)送郵件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07Android Studio中快捷鍵實(shí)現(xiàn)try catch等功能包含代碼塊的實(shí)現(xiàn)方法
這篇文章主要介紹了 Android Studio中快捷鍵實(shí)現(xiàn)try catch等功能包含代碼塊的實(shí)現(xiàn)方法的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-09-09Android貝塞爾曲線實(shí)現(xiàn)加入購物車拋物線動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了Android貝塞爾曲線實(shí)現(xiàn)加入購物車拋物線動(dòng)畫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06Android隱藏和沉浸式虛擬按鍵NavigationBar的實(shí)現(xiàn)方法
今天小編就為大家分享一篇Android隱藏和沉浸式虛擬按鍵NavigationBar的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-07-07Android中BaseActivity自定義標(biāo)題欄
這篇文章主要介紹了Android中BaseActivity自定義標(biāo)題欄,非常實(shí)用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01