Android使用RecyclerView实现今日头条频道管理功能

发布时间 - 2026-01-11 02:28:13    点击率:

使用过今日头条的伙计们对这个效果肯定很熟悉。拖拽可排序,点击标签后可以删除。今天我们采用RecyclerView来实现。

实现思路:

通过ItemTouchHelper来绑定RecyclerView的子控件触摸事件。
当滑动拖拽的时候,通知适配器来交换两个子控件的显示位置。
更改数据源,使数据源与子空间显示内容一致。
这就是实现的基本思路,是不是很简单?当然,首先要了解一下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文档的介绍,这个哥们儿是为RecyclerView工作的,他需要一个CallBack,可以回调RecyclerView的子控件滑动和拖拽事件,而且也可以通过这个CallBack重绘我们的子view。这就一目了然了嘛。通过使用ItemTouchHelper,可以很轻松的就实现了RecyclerView触摸事件的回调。换句话说,只要我们为RecyclerView 绑定了ItemTouchHelper之后,RecyclerView子控件的拖动和滑动事件已经帮我们实现了。我们所要做的就是在触摸事件之后,如何去改变去更新Adapter和改变我们的数据。

开始撸码:

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);
    //关联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("热点", 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();
    //滑动事件回调到了Adapter,用来处理数据
    touchInterface.onMove(position, position_target);
    return true;
  }


  //标签动画持续时间,默认是250
  @Override
  public long getAnimationDuration(RecyclerView recyclerView, int animationType, float animateDx, float animateDy) {

    return super.getAnimationDuration(recyclerView, animationType, animateDx, animateDy);
  }


  /**
   * 是否可以长按拖拽,默认是true
   *
   * @return
   */
  @Override
  public boolean isLongPressDragEnabled() {
    return super.isLongPressDragEnabled();
  }

  /**
   * 标签划出去的回调,direction是滑动的方向
   *
   * @return
   */

  @Override
  public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
  }

}


Adapter类中处理数据

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方法,例如:我们的数据是[1,2,3,4,5,6],当6移动到3的位置时,那么数据源最后变化为[1,2,6,3,4,5]。但是在显示的时候我们先是将当前position和targetposition对调[1,2,6,4,5,3],然后取出[4,5,3]进行右移一位,这样数据源就对上了。如果是从3移动到6进行左移就可以了,数据排序的算法,采用反转的思想。

public class DataUtils {

  /**
   * 利用反转的思想对数据进行排序
   * 例如:list{0,1,2,3,4,5,6,7} 左移一位
   * 第一步:第一位先反转{0,1,2,3,4,5,6,7}
   * 第二部:剩下的在反转{0,7,6,5,4,3,2,1}
   * 第三步:全部反转{1,2,3,4,5,6,7,0}
   *
   * 例如:list{0,1,2,3,4,5,6,7} 右移一位
   * 第一步:最右边一位先反转{1,2,3,4,5,6,7}
   * 第二部:剩下的在反转{6,5,4,3,2,1,0,7}
   * 第三步:全部反转{7,6,5,4,3,2,1,0}
   *
   * 因为list的index是从0开始的,step要相应的-1
   * 优点:少创建对象,优化内存
   *
   * @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>


实现效果:

github地址:https://github.com/zhoukai1526/Channel

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


# Android  # 今日头条  # 频道管理  # android自定义view仿今日头条加载文字变色效果  # Android仿今日头条顶部导航栏效果的实例代码  # Android仿今日头条多个fragment懒加载的实现  # Android studio导入项目的方法详解(简单快速)  # Android 仿今日头条简单的刷新效果实例代码  # Android仿今日头条APP实现下拉导航选择菜单效果  # Android应用中仿今日头条App制作ViewPager指示器  # Android实现仿网易今日头条等自定义频道listview 或者grideview等item上移到  # Android仿今日头条滑动页面导航效果  # Android实现今日头条订阅频道效果  # 拖拽  # 回调  # 是从  # 第二部  # 第三步  # 哥们儿  # 实现了  # 有什么  # 上了  # 这就是  # 这就  # 可以通过  # 很简单  # 自定义  # 拖动  # 定了  # 要了  # 就对  # 来实现  # 使用过 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 如何快速重置建站主机并恢复默认配置?  Laravel怎么解决跨域问题_Laravel配置CORS跨域访问  Laravel如何处理和验证JSON类型的数据库字段  如何在建站宝盒中设置产品搜索功能?  微信小程序 五星评分(包括半颗星评分)实例代码  深圳网站制作平台,深圳市做网站好的公司有哪些?  Python文件异常处理策略_健壮性说明【指导】  哪家制作企业网站好,开办像阿里巴巴那样的网络公司和网站要怎么做?  EditPlus中的正则表达式实战(5)  Laravel如何使用查询构建器?(Query Builder高级用法)  智能起名网站制作软件有哪些,制作logo的软件?  如何正确下载安装西数主机建站助手?  EditPlus中的正则表达式 实战(4)  Laravel控制器是什么_Laravel MVC架构中Controller的作用与实践  如何在万网自助建站平台快速创建网站?  如何快速完成中国万网建站详细流程?  合肥制作网站的公司有哪些,合肥聚美网络科技有限公司介绍?  php结合redis实现高并发下的抢购、秒杀功能的实例  Swift开发中switch语句值绑定模式  Android使用GridView实现日历的简单功能  LinuxCD持续部署教程_自动发布与回滚机制  Laravel怎么处理异常_Laravel自定义异常处理与错误页面教程  Linux后台任务运行方法_nohup与&使用技巧【技巧】  如何破解联通资金短缺导致的基站建设难题?  如何用AI一键生成爆款短视频文案?小红书AI文案写作指令【教程】  儿童网站界面设计图片,中国少年儿童教育网站-怎么去注册?  如何在云虚拟主机上快速搭建个人网站?  Laravel如何实现邮箱地址验证功能_Laravel邮件验证流程与配置  Laravel如何实现用户角色和权限系统_Laravel角色权限管理机制  使用PHP下载CSS文件中的所有图片【几行代码即可实现】  魔毅自助建站系统:模板定制与SEO优化一键生成指南  轻松掌握MySQL函数中的last_insert_id()  JS实现鼠标移上去显示图片或微信二维码  Win11怎么设置虚拟桌面 Win11新建多桌面切换操作【技巧】  厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?  原生JS获取元素集合的子元素宽度实例  Zeus浏览器网页版官网入口 宙斯浏览器官网在线通道  Laravel如何安装Breeze扩展包_Laravel用户注册登录功能快速实现【流程】  如何在腾讯云服务器快速搭建个人网站?  ChatGPT 4.0官网入口地址 ChatGPT在线体验官网  Laravel怎么进行浏览器测试_Laravel Dusk自动化浏览器测试入门  Win11怎么关闭资讯和兴趣_Windows11任务栏设置隐藏小组件  ChatGPT常用指令模板大全 新手快速上手的万能Prompt合集  Windows驱动无法加载错误解决方法_驱动签名验证失败处理步骤  phpredis提高消息队列的实时性方法(推荐)  laravel怎么通过契约(Contracts)编程_laravel契约(Contracts)编程方法  长沙做网站要多少钱,长沙国安网络怎么样?  Win11搜索不到蓝牙耳机怎么办 Win11蓝牙驱动更新修复【详解】  如何利用DOS批处理实现定时关机操作详解  详解Android中Activity的四大启动模式实验简述