Android 高仿今日头条效果频道拖拽效果

  • Post author:
  • Post category:其他




仿今日头条频道拖拽效果

描述:这是一个超级高仿的今日头条效果的控件。

Tips:本项目代码部分逻辑参考于其他文章,众人拾柴火焰高,希望大家能多多补充。



项目代码在最后面!!!!

跳转到最后



控件效果如下:

在这里插入图片描述



实现功能:

  • 我的频道item在抖动
  • 编辑模式和非编辑模式切换
  • 从我的频道拖拽item到其他频道自动添加
  • 双方频道点击添加
  • 添加时移动效果
  • 已经封装到Dialog中



设计核心:

主要的设计核心是依赖于itemtouchhelper进行拖拽,所有item使用一个recyclerview进行封装,



核心代码:

因为涉及到各类文件包括layout,anim,drawable,这里就不放出全部代码,仅放出核心部分代码以作参考。

拖拽排序 + 增删适配器

package com.ui.design.view.JRTTChannel.demochannel;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.core.view.MotionEventCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;


import com.ui.design.R;
import com.ui.design.view.JRTTChannel.helper.OnDragVHListener;
import com.ui.design.view.JRTTChannel.helper.OnItemMoveListener;

import java.util.ArrayList;
import java.util.List;


/**
 * 拖拽排序 + 增删
 * tangxianfeng 2021.7.19
 */
public class ChannelAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements OnItemMoveListener {
    // 我的频道 标题部分
    public static final int TYPE_MY_CHANNEL_HEADER = 0;
    // 我的频道
    public static final int TYPE_MY = 1;
    // 其他频道 标题部分
    public static final int TYPE_OTHER_CHANNEL_HEADER = 2;
    // 其他频道
    public static final int TYPE_OTHER = 3;

    // 我的频道之前的header数量  该demo中 即标题部分 为 1
    private static final int COUNT_PRE_MY_HEADER = 1;
    // 其他频道之前的header数量  该demo中 即标题部分 为 COUNT_PRE_MY_HEADER + 1
    private static final int COUNT_PRE_OTHER_HEADER = COUNT_PRE_MY_HEADER + 1;

    private static final long ANIM_TIME = 360L;

    // touch 点击开始时间
    private long startTime;
    // touch 间隔时间  用于分辨是否是 "点击"
    private static final long SPACE_TIME = 100;

    private LayoutInflater mInflater;
    private ItemTouchHelper mItemTouchHelper;

    // 是否为 编辑 模式
    private boolean isEditMode=false;

    private ViewGroup parentViw;

    private List<ChannelEntity> mMyChannelItems, mOtherChannelItems;

    // 我的频道点击事件
    private OnMyChannelItemClickListener mChannelItemClickListener;

    public ChannelAdapter(Context context, ItemTouchHelper helper, List<ChannelEntity> mMyChannelItems, List<ChannelEntity> mOtherChannelItems) {
        this.mInflater = LayoutInflater.from(context);
        this.mItemTouchHelper = helper;
        this.mMyChannelItems = mMyChannelItems;
        this.mOtherChannelItems = mOtherChannelItems;
    }

    @Override
    public int getItemViewType(int position) {
        if (position == 0) {    // 我的频道 标题部分
            return TYPE_MY_CHANNEL_HEADER;
        } else if (position == mMyChannelItems.size() + 1) {    // 其他频道 标题部分
            return TYPE_OTHER_CHANNEL_HEADER;
        } else if (position > 0 && position < mMyChannelItems.size() + 1) {
            return TYPE_MY;
        } else {
            return TYPE_OTHER;
        }
    }

    private MyViewHolder CurrentPostionView;

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
        final View view;
        this.parentViw = parent;
        switch (viewType) {
            case TYPE_MY_CHANNEL_HEADER:
                view = mInflater.inflate(R.layout.jrtt_channel_item_my_channel_header, parent, false);
                final MyChannelHeaderViewHolder holder = new MyChannelHeaderViewHolder(view);
                holder.tvBtnEdit.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (!isEditMode) {
                            startEditMode((RecyclerView) parent);
                            holder.tvBtnEdit.setText("完成");
                        } else {
                            cancelEditMode((RecyclerView) parent);
                            holder.tvBtnEdit.setText("编辑");
                        }
                    }
                });
                return holder;

            case TYPE_MY:
                view = mInflater.inflate(R.layout.jrtt_channel_item_my, parent, false);
                final MyViewHolder myHolder = new MyViewHolder(view);
                myHolder.setIsRecyclable(false);//取消复用
                myHolder.itemView.setOnClickListener(v -> {
                    int position = myHolder.getAdapterPosition();
                    if (isEditMode) {
                        RecyclerView recyclerView = ((RecyclerView) parent);
                        View targetView = recyclerView.getLayoutManager().findViewByPosition(mMyChannelItems.size() + COUNT_PRE_OTHER_HEADER);
                        View currentView = recyclerView.getLayoutManager().findViewByPosition(position);
                        // 如果targetView不在屏幕内,则indexOfChild为-1  此时不需要添加动画,因为此时notifyItemMoved自带一个向目标移动的动画
                        // 如果在屏幕内,则添加一个位移动画
                        if (recyclerView.indexOfChild(targetView) >= 0) {
                            int targetX, targetY;

                            RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
                            int spanCount = ((GridLayoutManager) manager).getSpanCount();

                            // 移动后 高度将变化 (我的频道Grid 最后一个item在新的一行第一个)
                            if ((mMyChannelItems.size() - COUNT_PRE_MY_HEADER) % spanCount == 0) {
                                View preTargetView = recyclerView.getLayoutManager().findViewByPosition(mMyChannelItems.size() + COUNT_PRE_OTHER_HEADER - 1);
                                targetX = preTargetView.getLeft();
                                targetY = preTargetView.getTop();
                            } else {
                                targetX = targetView.getLeft();
                                targetY = targetView.getTop();
                            }

                            moveMyToOther(myHolder);
                            startAnimation(recyclerView, currentView, targetX, targetY,true);

                        } else {
                            moveMyToOther(myHolder);
                        }
                    } else {
                        mChannelItemClickListener.onItemClick(v, position - COUNT_PRE_MY_HEADER);
                    }
                });

                myHolder.itemView.setOnLongClickListener(v -> {
                    if (!isEditMode) {
                        RecyclerView recyclerView = ((RecyclerView) parent);
                        startEditMode(recyclerView);

                        // header 按钮文字 改成 "完成"
                        View view1 = recyclerView.getChildAt(0);
                        if (view1 == recyclerView.getLayoutManager().findViewByPosition(0)) {
                            TextView tvBtnEdit = (TextView) view1.findViewById(R.id.tv_btn_edit);
                            tvBtnEdit.setText("完成");
                        }
                    }
                    mItemTouchHelper.startDrag(myHolder);
                    return true;
                });

                myHolder.itemView.setOnTouchListener(new View.OnTouchListener() {
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        if (isEditMode) {
                            CurrentPostionView = myHolder;
                            switch (MotionEventCompat.getActionMasked(event)) {
                                case MotionEvent.ACTION_DOWN:
                                    startTime = System.currentTimeMillis();
                                    break;
                                case MotionEvent.ACTION_MOVE:
                                    if (System.currentTimeMillis() - startTime > SPACE_TIME) {
                                        mItemTouchHelper.startDrag(myHolder);
                                    }
                                    break;
                                case MotionEvent.ACTION_CANCEL:
                                case MotionEvent.ACTION_UP:
                                    startTime = 0;
                                    break;
                            }
                        }
                        return false;
                    }
                });

                ShakeView(myHolder.itemView, isEditMode);

                return myHolder;

            case TYPE_OTHER_CHANNEL_HEADER:
                view = mInflater.inflate(R.layout.jrtt_channel_item_other_channel_header, parent, false);
                return new RecyclerView.ViewHolder(view) {
                };

            case TYPE_OTHER:
                view = mInflater.inflate(R.layout.jrtt_channel__item_other, parent, false);
                final OtherViewHolder otherHolder = new OtherViewHolder(view);
                otherHolder.itemView.setOnClickListener(v -> {
                    RecyclerView recyclerView = ((RecyclerView) parent);
                    RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
                    int currentPiosition = otherHolder.getAdapterPosition();
                    // 如果RecyclerView滑动到底部,移动的目标位置的y轴 - height
                    View currentView = manager.findViewByPosition(currentPiosition);
                    // 目标位置的前一个item  即当前MyChannel的最后一个
                    View preTargetView = manager.findViewByPosition(mMyChannelItems.size() - 1 + COUNT_PRE_MY_HEADER);

                    // 如果targetView不在屏幕内,则为-1  此时不需要添加动画,因为此时notifyItemMoved自带一个向目标移动的动画
                    // 如果在屏幕内,则添加一个位移动画
                    if (recyclerView.indexOfChild(preTargetView) >= 0) {
                        int targetX = preTargetView.getLeft();
                        int targetY = preTargetView.getTop();

                        int targetPosition = mMyChannelItems.size() - 1 + COUNT_PRE_OTHER_HEADER;

                        GridLayoutManager gridLayoutManager = ((GridLayoutManager) manager);
                        int spanCount = gridLayoutManager.getSpanCount();
                        // target 在最后一行第一个
                        if ((targetPosition - COUNT_PRE_MY_HEADER) % spanCount == 0) {
                            View targetView = manager.findViewByPosition(targetPosition);
                            targetX = targetView.getLeft();
                            targetY = targetView.getTop();
                        } else {
                            targetX += preTargetView.getWidth();

                            // 最后一个item可见
                            if (gridLayoutManager.findLastVisibleItemPosition() == getItemCount() - 1) {
                                // 最后的item在最后一行第一个位置
                                if ((getItemCount() - 1 - mMyChannelItems.size() - COUNT_PRE_OTHER_HEADER) % spanCount == 0) {
                                    // RecyclerView实际高度 > 屏幕高度 && RecyclerView实际高度 < 屏幕高度 + item.height
                                    int firstVisiblePostion = gridLayoutManager.findFirstVisibleItemPosition();
                                    if (firstVisiblePostion == 0) {
                                        // FirstCompletelyVisibleItemPosition == 0 即 内容不满一屏幕 , targetY值不需要变化
                                        // // FirstCompletelyVisibleItemPosition != 0 即 内容满一屏幕 并且 可滑动 , targetY值 + firstItem.getTop
                                        if (gridLayoutManager.findFirstCompletelyVisibleItemPosition() != 0) {
                                            int offset = (-recyclerView.getChildAt(0).getTop()) - recyclerView.getPaddingTop();
                                            targetY += offset;
                                        }
                                    } else { // 在这种情况下 并且 RecyclerView高度变化时(即可见第一个item的 position != 0),
                                        // 移动后, targetY值  + 一个item的高度
                                        targetY += preTargetView.getHeight();
                                    }
                                }
                            } else {
                                System.out.println("current--No");
                            }
                        }

                        // 如果当前位置是otherChannel可见的最后一个
                        // 并且 当前位置不在grid的第一个位置
                        // 并且 目标位置不在grid的第一个位置

                        // 则 需要延迟250秒 notifyItemMove , 这是因为这种情况 , 并不触发ItemAnimator , 会直接刷新界面
                        // 导致我们的位移动画刚开始,就已经notify完毕,引起不同步问题
                        if (currentPiosition == gridLayoutManager.findLastVisibleItemPosition()
                                && (currentPiosition - mMyChannelItems.size() - COUNT_PRE_OTHER_HEADER) % spanCount != 0
                                && (targetPosition - COUNT_PRE_MY_HEADER) % spanCount != 0) {
                            moveOtherToMyWithDelay(otherHolder);
                        } else {
                            moveOtherToMy(otherHolder);
                        }
                        startAnimation(recyclerView, currentView, targetX, targetY,false);
                    } else {
                        moveOtherToMy(otherHolder);
                    }
                });
                return otherHolder;
        }
        return null;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof MyViewHolder) {

            MyViewHolder myHolder = (MyViewHolder) holder;
            myHolder.textView.setText(mMyChannelItems.get(position - COUNT_PRE_MY_HEADER).getName());
            if (isEditMode) {
                myHolder.imgEdit.setVisibility(View.VISIBLE);
            } else {
                myHolder.imgEdit.setVisibility(View.INVISIBLE);
            }

        } else if (holder instanceof OtherViewHolder) {

            ((OtherViewHolder) holder).textView.setText(mOtherChannelItems.get(position - mMyChannelItems.size() - COUNT_PRE_OTHER_HEADER).getName());

        } else if (holder instanceof MyChannelHeaderViewHolder) {

            MyChannelHeaderViewHolder headerHolder = (MyChannelHeaderViewHolder) holder;
            if (isEditMode) {
                headerHolder.tvBtnEdit.setText("完成");
            } else {
                headerHolder.tvBtnEdit.setText("编辑");
            }
        }
    }


    @Override
    public int getItemCount() {
        // 我的频道  标题 + 我的频道.size + 其他频道 标题 + 其他频道.size
        return mMyChannelItems.size() + mOtherChannelItems.size() + COUNT_PRE_OTHER_HEADER;
    }

    public void reset(){
        if (isEditMode){
            isEditMode=false;
            notifyDataSetChanged();
        }
    }//重置,避免重新打开不抖动还显示编辑

    public List<ChannelEntity> getMyChannelList(){
        return mMyChannelItems;
    }

    public List<ChannelEntity> getmOtherChannelItems(){
        return mOtherChannelItems;
    }


    public List<ChannelEntity> getChannelList(){
        List<ChannelEntity> all=new ArrayList<>();
        all.addAll(mMyChannelItems);
        all.addAll(mOtherChannelItems);
        return all;
    }
    /**
     * 开始增删动画
     */
    private void startAnimation(RecyclerView recyclerView, final View currentView, float targetX, float targetY,boolean isfromme) {
        final ViewGroup viewGroup = (ViewGroup) recyclerView.getParent();
        final ImageView mirrorView = addMirrorView(viewGroup, recyclerView, currentView);

        Animation animation = getTranslateAnimator(
                targetX - currentView.getLeft(), targetY - currentView.getTop());
        currentView.setVisibility(View.INVISIBLE);
        mirrorView.startAnimation(animation);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                viewGroup.removeView(mirrorView);
                if (currentView.getVisibility() == View.INVISIBLE) {
                    currentView.setVisibility(View.VISIBLE);
                    //ShakeView(currentView,isEditMode&&isfromme);
                }
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
    }

    /**
     * 我的频道 移动到 其他频道
     *
     * @param myHolder
     */
    private void moveMyToOther(MyViewHolder myHolder) {
        int position = myHolder.getAdapterPosition();

        int startPosition = position - COUNT_PRE_MY_HEADER;
        if (startPosition > mMyChannelItems.size() - 1) {
            return;
        }
        ChannelEntity item = mMyChannelItems.get(startPosition);
        mMyChannelItems.remove(startPosition);
        mOtherChannelItems.add(0, item);

        notifyItemMoved(position, mMyChannelItems.size() + COUNT_PRE_OTHER_HEADER);
    }

    /**
     * 其他频道 移动到 我的频道
     *
     * @param otherHolder
     */
    private void moveOtherToMy(OtherViewHolder otherHolder) {
        int position = processItemRemoveAdd(otherHolder);
        if (position == -1) {
            return;
        }
        notifyItemMoved(position, mMyChannelItems.size() - 1 + COUNT_PRE_MY_HEADER);

    }

    /**
     * 其他频道 移动到 我的频道 伴随延迟
     *
     * @param otherHolder
     */
    private void moveOtherToMyWithDelay(OtherViewHolder otherHolder) {
        final int position = processItemRemoveAdd(otherHolder);
        if (position == -1) {
            return;
        }
        delayHandler.postDelayed(() -> notifyItemMoved(position, mMyChannelItems.size() - 1 + COUNT_PRE_MY_HEADER), ANIM_TIME);
    }

    private final Handler delayHandler = new Handler();

    private int processItemRemoveAdd(OtherViewHolder otherHolder) {
        int position = otherHolder.getAdapterPosition();

        int startPosition = position - mMyChannelItems.size() - COUNT_PRE_OTHER_HEADER;
        if (startPosition > mOtherChannelItems.size() - 1) {
            return -1;
        }
        ChannelEntity item = mOtherChannelItems.get(startPosition);
        mOtherChannelItems.remove(startPosition);
        mMyChannelItems.add(item);
        return position;
    }


    /**
     * 添加需要移动的 镜像View
     */
    private ImageView addMirrorView(ViewGroup parent, RecyclerView recyclerView, View view) {
        /**
         * 我们要获取cache首先要通过setDrawingCacheEnable方法开启cache,然后再调用getDrawingCache方法就可以获得view的cache图片了。
         buildDrawingCache方法可以不用调用,因为调用getDrawingCache方法时,若果cache没有建立,系统会自动调用buildDrawingCache方法生成cache。
         若想更新cache, 必须要调用destoryDrawingCache方法把旧的cache销毁,才能建立新的。
         当调用setDrawingCacheEnabled方法设置为false, 系统也会自动把原来的cache销毁。
         */
        view.destroyDrawingCache();
        view.setDrawingCacheEnabled(true);
        final ImageView mirrorView = new ImageView(recyclerView.getContext());
        Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
        mirrorView.setImageBitmap(bitmap);
        view.setDrawingCacheEnabled(false);
        int[] locations = new int[2];
        view.getLocationOnScreen(locations);
        int[] parenLocations = new int[2];
        recyclerView.getLocationOnScreen(parenLocations);
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(bitmap.getWidth(), bitmap.getHeight());
        params.setMargins(locations[0], locations[1] - parenLocations[1], 0, 0);
        parent.addView(mirrorView, params);

        return mirrorView;
    }

    @Override
    public void onItemMove(int fromPosition, int toPosition) {
        ChannelEntity item = mMyChannelItems.get(fromPosition - COUNT_PRE_MY_HEADER);
        mMyChannelItems.remove(fromPosition - COUNT_PRE_MY_HEADER);
        mMyChannelItems.add(toPosition - COUNT_PRE_MY_HEADER, item);
        notifyItemMoved(fromPosition, toPosition);
    }

    @Override
    public void onChangeItem() {
        int postion = CurrentPostionView.getAdapterPosition();
        RecyclerView recyclerView = ((RecyclerView) parentViw);
        View targetView = recyclerView.getLayoutManager().findViewByPosition(mMyChannelItems.size() + COUNT_PRE_OTHER_HEADER);
        View currentView = recyclerView.getLayoutManager().findViewByPosition(postion);
        // 如果targetView不在屏幕内,则indexOfChild为-1  此时不需要添加动画,因为此时notifyItemMoved自带一个向目标移动的动画
        // 如果在屏幕内,则添加一个位移动画
        if (recyclerView.indexOfChild(targetView) >= 0) {
            int targetX, targetY;

            RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
            int spanCount = ((GridLayoutManager) manager).getSpanCount();

            // 移动后 高度将变化 (我的频道Grid 最后一个item在新的一行第一个)
            if ((mMyChannelItems.size() - COUNT_PRE_MY_HEADER) % spanCount == 0) {
                View preTargetView = recyclerView.getLayoutManager().findViewByPosition(mMyChannelItems.size() + COUNT_PRE_OTHER_HEADER - 1);
                targetX = preTargetView.getLeft();
                targetY = preTargetView.getTop();
            } else {
                targetX = targetView.getLeft();
                targetY = targetView.getTop();
            }

            moveMyToOther(CurrentPostionView);
            startAnimation(recyclerView, currentView, targetX, targetY,false);

        } else {
            moveMyToOther(CurrentPostionView);
        }

    }

    /**
     * 开启编辑模式
     *
     * @param parent
     */
    private void startEditMode(RecyclerView parent) {
        isEditMode = true;

        int visibleChildCount = parent.getChildCount();
        for (int i = 0; i < visibleChildCount; i++) {
            View view = parent.getChildAt(i);
            ImageView imgEdit = (ImageView) view.findViewById(R.id.img_edit);
            RelativeLayout itemView = (RelativeLayout) view.findViewById(R.id.my_rela_item);
            TextView edittips=(TextView)view.findViewById(R.id.edit_tips_text);
            if (edittips!=null){
                edittips.setVisibility(View.VISIBLE);
            }
            if (itemView != null) {
                Log.e("ChannelTest","1111"+i);
                ShakeView(itemView, true);
            }
            if (imgEdit != null) {
                Log.e("ChannelTest","2222"+i);
                imgEdit.setVisibility(View.VISIBLE);
            }
        }
    }

    /**
     * 完成编辑模式
     *
     * @param parent
     */
    private void cancelEditMode(RecyclerView parent) {
        isEditMode = false;

        int visibleChildCount = parent.getChildCount();
        for (int i = 0; i < visibleChildCount; i++) {
            View view = parent.getChildAt(i);
            ImageView imgEdit = (ImageView) view.findViewById(R.id.img_edit);
            RelativeLayout itemView = (RelativeLayout) view.findViewById(R.id.my_rela_item);
            TextView edittips=(TextView)view.findViewById(R.id.edit_tips_text);
            if (edittips!=null){
                edittips.setVisibility(View.INVISIBLE);
            }
            if (itemView != null) {
                ShakeView(itemView, false);
            }
            if (imgEdit != null) {
                imgEdit.setVisibility(View.INVISIBLE);
            }
        }
    }

    /**
     * View抖动效果
     *
     * @param view
     * @param shake
     */
    private void ShakeView(View view, boolean shake) {
        Log.e("TESTChannel","ViewShakeis"+shake);
        if (shake) {
            if (view.getAnimation()!=null){
                view.getAnimation().start();
            }
            RotateAnimation rotate = new RotateAnimation(-1, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            LinearInterpolator lin = new LinearInterpolator();
            rotate.setInterpolator(lin);
            rotate.setDuration(100);//设置动画持续时间
            rotate.setRepeatCount(-1);//设置重复次数
            rotate.setFillAfter(false);//动画执行完后是否停留在执行完的状态
            rotate.setStartOffset(10);//执行前的等待时间
            rotate.setRepeatMode(Animation.REVERSE);
            view.startAnimation(rotate);
        } else {
            if (view.getAnimation()!=null){
                view.getAnimation().cancel();
            }
            view.clearAnimation();
        }
    }


    /**
     * 获取位移动画
     */
    private TranslateAnimation getTranslateAnimator(float targetX, float targetY) {
        TranslateAnimation translateAnimation = new TranslateAnimation(
                Animation.RELATIVE_TO_SELF, 0f,
                Animation.ABSOLUTE, targetX,
                Animation.RELATIVE_TO_SELF, 0f,
                Animation.ABSOLUTE, targetY);
        // RecyclerView默认移动动画250ms 这里设置360ms 是为了防止在位移动画结束后 remove(view)过早 导致闪烁
        translateAnimation.setDuration(ANIM_TIME);
        translateAnimation.setFillAfter(true);
        return translateAnimation;
    }

    interface OnMyChannelItemClickListener {
        void onItemClick(View v, int position);
    }

    public void setOnMyChannelItemClickListener(OnMyChannelItemClickListener listener) {
        this.mChannelItemClickListener = listener;
    }

    /**
     * 我的频道
     */
    class MyViewHolder extends RecyclerView.ViewHolder implements OnDragVHListener {
        private TextView textView;
        private ImageView imgEdit;

        public MyViewHolder(View itemView) {
            super(itemView);
            textView = (TextView) itemView.findViewById(R.id.tv);
            imgEdit = (ImageView) itemView.findViewById(R.id.img_edit);
        }

        /**
         * item 被选中时
         */
        @Override
        public void onItemSelected() {
            textView.setBackgroundResource(R.drawable.jrtt_channel_bg_channel_p);
        }

        /**
         * item 取消选中时
         */
        @Override
        public void onItemFinish() {
            textView.setBackgroundResource(R.drawable.jrtt_channel_bg_channel);
        }
    }

    /**
     * 其他频道
     */
    class OtherViewHolder extends RecyclerView.ViewHolder {
        private TextView textView;

        public OtherViewHolder(View itemView) {
            super(itemView);
            textView = (TextView) itemView.findViewById(R.id.tv);
        }
    }

    /**
     * 我的频道  标题部分
     */
    class MyChannelHeaderViewHolder extends RecyclerView.ViewHolder {
        private TextView tvBtnEdit;

        public MyChannelHeaderViewHolder(View itemView) {
            super(itemView);
            tvBtnEdit = (TextView) itemView.findViewById(R.id.tv_btn_edit);
        }
    }
}

频道实体类

package com.ui.design.view.JRTTChannel.demochannel;

/**
 * 频道实体类
 * tangxianfeng 2021.7.19
 */
public class ChannelEntity {

    private long id;
    private String name;

    public ChannelEntity(String name) {
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

拖拽回调

package com.ui.design.view.JRTTChannel.helper;

import android.util.Log;

import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;

import com.ui.design.view.JRTTChannel.demochannel.ChannelAdapter;

/**
 * 拖拽回调
 * tangxianfeng 2021.7.19
 */
public class ItemDragHelperCallback extends ItemTouchHelper.Callback {

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags;
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        if (manager instanceof GridLayoutManager || manager instanceof StaggeredGridLayoutManager) {
            dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
        } else {
            dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        }
        // 如果想支持滑动(删除)操作, swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END
        int swipeFlags = 0;
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {

        // 不同Type之间不可移动
        Log.e("Test",target.getItemViewType()+"");
        if (viewHolder.getItemViewType() != target.getItemViewType()) {
            if (recyclerView.getAdapter() instanceof OnItemMoveListener && target.getItemViewType() == ChannelAdapter.TYPE_OTHER) {
                OnItemMoveListener listener = ((OnItemMoveListener) recyclerView.getAdapter());
                listener.onChangeItem();
            }
            return false;
        }

        if (recyclerView.getAdapter() instanceof OnItemMoveListener) {
            OnItemMoveListener listener = ((OnItemMoveListener) recyclerView.getAdapter());
            listener.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        }
        return true;
    }

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

    }

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        // 不在闲置状态
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            if (viewHolder instanceof OnDragVHListener) {
                OnDragVHListener itemViewHolder = (OnDragVHListener) viewHolder;
                itemViewHolder.onItemSelected();
            }
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        if (viewHolder instanceof OnDragVHListener) {
            OnDragVHListener itemViewHolder = (OnDragVHListener) viewHolder;
            itemViewHolder.onItemFinish();
        }
        super.clearView(recyclerView, viewHolder);
    }

    @Override
    public boolean isLongPressDragEnabled() {
        // 不支持长按拖拽功能 手动控制
        return false;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        // 不支持滑动功能
        return false;
    }
}

触发监听器

package com.ui.design.view.JRTTChannel.helper;

/**
 * ViewHolder 被选中 以及 拖拽释放 触发监听器
 * tangxianfeng 2021.7.19.
 */
public interface OnDragVHListener {
    /**
     * Item被选中时触发
     */
    void onItemSelected();


    /**
     * Item在拖拽结束/滑动结束后触发
     */
    void onItemFinish();
}

移动监听器

package com.ui.design.view.JRTTChannel.helper;

/**
 * Item移动后 触发
 * tangxianfeng 2021.7.19
 */
public interface OnItemMoveListener {
    void onItemMove(int fromPosition, int toPosition);
    void onChangeItem();
}

频道管理弹窗

package com.ui.design.view.JRTTChannel.demochannel;

import android.app.Dialog;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;

import com.ui.design.R;
import com.ui.design.view.JRTTChannel.helper.ItemDragHelperCallback;

import java.util.ArrayList;
import java.util.List;

/**
 * 频道管理弹窗
 * tangxianfeng 2021.7.19
 */
public class ChannelDialog extends Dialog {


    private RecyclerView mRecy;
    private final Context mContext;
    private ChannelAdapter adapter;
    List<ChannelEntity> items = new ArrayList<>();
    List<ChannelEntity> otherItems = new ArrayList<>();
    private final BackToChannel backToChannel;

    public ChannelDialog(@NonNull Context context, BackToChannel backToChannel) {
        super(context);
        this.mContext = context;
        this.backToChannel = backToChannel;
    }

    /**
     * 初始化信息
     */
    private void initDataInfo() {
        for (int i=0;i<11;i++){
            items.add(new ChannelEntity("频道"+i));
            otherItems.add(new ChannelEntity("其他"+i));
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.jrtt_channel_activity_channel);
        //按空白处不能取消动画
        setCanceledOnTouchOutside(true);
        //设置window背景,默认的背景会有Padding值,不能全屏。当然不一定要是透明,你可以设置其他背景,替换默认的背景即可。
        getWindow().setBackgroundDrawable(new ColorDrawable(Color.WHITE));
        //一定要在setContentView之后调用,否则无效
        getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        getWindow().setGravity(Gravity.BOTTOM);
        getWindow().setWindowAnimations(R.style.BottomInAndOutStyle);
        mRecy = (RecyclerView) findViewById(R.id.recy);

        initDataInfo();
        init();
        findViewById(R.id.channel_close).setOnClickListener(v -> {
            dismiss();
        });
    }



    @Override
    public void dismiss() {
        super.dismiss();
        adapter.reset();
        //savaData();
        backToChannel.back(adapter.getMyChannelList());
    }

    private void init() {

        GridLayoutManager manager = new GridLayoutManager(mContext, 3);
        mRecy.setLayoutManager(manager);

        ItemDragHelperCallback callback = new ItemDragHelperCallback();
        final ItemTouchHelper helper = new ItemTouchHelper(callback);
        helper.attachToRecyclerView(mRecy);

        adapter = new ChannelAdapter(mContext, helper, items, otherItems);
        manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                int viewType = adapter.getItemViewType(position);
                return viewType == ChannelAdapter.TYPE_MY || viewType == ChannelAdapter.TYPE_OTHER ? 1 : 3;
            }
        });
        mRecy.setAdapter(adapter);

        adapter.setOnMyChannelItemClickListener((v, position) -> Toast.makeText(mContext, items.get(position).getName(), Toast.LENGTH_SHORT).show());
    }


    @Override
    public void show() {
        if (isShowing()) {
            return;
        }
        super.show();
    }

    public interface BackToChannel {
        void back(List<ChannelEntity> entities);
    }
}



项目代码仓库




UIDesign 开源项目



版权声明:本文为number_cmd9原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。