由于项目中需要实现列表可长按条目拖动改变位置的效果,网上有很多成型的demo,写的都非常好,自己下载研究后,自己模仿着写了一个,以下作为整理:

前言
  • 预期效果
    列表形式、长按条目放大动画,拖动修改列表中item位置,松开时候显示缩小动画
  • 思路
    列表使用RecycleView,动画使用ScaleAnimation(缩放动画),拖动功能使用ItemTouchHelper
知识点讲解
  • ScaleAnimation缩放动画的使用
ScaleAnimation mScaleAnimation=new ScaleAnimation(float fromX,  
         float toX, float fromY, float toY,int pivotXType, float pivotXValue,  
         int pivotYType, float pivotYValue) 

fromX:动画开始前在X坐标的大小。
toX:动画结束后在X坐标的大小。
fromY:动画开始前在Y坐标的大小。
toY:动画结束后在Y坐标的大小。
pivotXType:缩放中心点的X坐标类型。取值范围为ABSOLUTE、RELATIVE_TO_SELF、 RELATIVE_TO_PARENT。
pivotXValue:缩放中心点的X坐标值。当pivotXType==ABSOLUTE时,表示绝对位置;否则表示相对位置,1.0表示100%。
pivotYType:缩放中心点的Y坐标类型。
pivotYValue:缩放中心点的Y坐标。

Java代码使用:

//放大效果: 
 private var startAnimation: ScaleAnimation? = ScaleAnimation( 
        1.0f, 
        1.1f, 
        1.0f, 
        1.1f, 
        Animation.RELATIVE_TO_SELF, 
        0.5f, 
        Animation.RELATIVE_TO_SELF, 
        0.5f 
    ) 
//从放大恢复至原来的效果 
 private var endAnimation: ScaleAnimation? = ScaleAnimation( 
        1.1f, 
        1.0f, 
        1.1f, 
        1.0f, 
        Animation.RELATIVE_TO_SELF, 
        0.5f, 
        Animation.RELATIVE_TO_SELF, 
        0.5f 
    ) 
  • ItemTouchHelper
    作为RecyclerView的辅助类ItemTouchHelper,在实现侧滑删除和拖动交换位置的时候便展示出举足轻重的作用。
    ItemTouchHelper继承了ItemDecoration
public class ItemTouchHelper extends RecyclerView.ItemDecoration 
        implements RecyclerView.OnChildAttachStateChangeListener {
   
     
} 

ItemTouchHelper的使用:

var mDragItemTouchHelper =ItemTouchHelper(object : ItemTouchHelper.Callback() {
   
     
 
       /** 
        * 在此方法里面我们需要构建两个flag,一个是dragFlags,表示拖动效果支持的方向, 
        * 另一个是swipeFlags,表示侧滑效果支持的方向。在我们的Demo中,拖动执行上下两个方向, 
        * 侧滑执行左右两个方向,这些操作我们都可以在此方法里面定义。 
        */ 
       override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: ViewHolder): Int {
   
     
           //上下滑动 
           val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN 
           //向左滑动 
           val swipeFlags = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT 
           //return makeMovementFlags(dragFlags, swipeFlags) 
              return makeMovementFlags(dragFlags, 0)//此处禁止侧滑删除 
       } 
 
       /** 
        * 当拖动效果已经产生了,会回调此方法。在此方法里面,我们通常会更新数据源, 
        * 比如说,一个ItemView从0拖到了1位置,那么对应的数据源也需要更改位置。 
        */ 
       override fun onMove( 
           recyclerView: RecyclerView, 
           viewHolder: ViewHolder, 
           target: ViewHolder 
       ): Boolean {
   
     
 
           oldPosition=viewHolder.adapterPosition 
           toPosition=target.adapterPosition 
 
           return  mItemTouchStatus!!.onItemMove(viewHolder.adapterPosition, target.adapterPosition) 
       } 
 
       /** 
        * 当侧滑效果以上产生了,会回调此方法。在此方法里面,我们也会更新数据源。 
        * 与onMove方法不同到的是,我们在这个方法里面从数据源里面移除相应的数据, 
        * 然后调用notifyXXX方法就行了。 
        */ 
       override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
   
     
       } 
 
       override fun onSelectedChanged(viewHolder: ViewHolder?, actionState: Int) {
   
     
           /** 
            * actionState: 
            * 1.ACTION_STATE_IDLE表示没有任何手势,此时selected对应的应当是null; 
            * 2. ACTION_STATE_SWIPE表示当前ItemView处于侧滑状态; 
            * 3. ACTION_STATE_DRAG表示当前ItemView处于拖动状态。 
            */ 
           if (showDragAnimation && actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
   
     
               startAnimation(viewHolder!!.itemView) 
           } 
           super.onSelectedChanged(viewHolder, actionState) 
       } 
 
       override fun clearView(recyclerView: RecyclerView, viewHolder: ViewHolder) {
   
     
           super.clearView(recyclerView, viewHolder) 
           if (showDragAnimation) {
   
     
               //操作结束后会走这个方法 
               removeView(viewHolder.itemView) 
           } 
       } 
 
   }) 
完整代码:

DragRecycleView.java

internal class DragRecycleView : RecyclerView {
   
     
    //是否允许拖动 
    var dragEnable = true 
 
    //是否显示动画 
    var showDragAnimation = true 
 
    //事件监听 
    var mItemTouchStatus: ItemTouchStatues? = null 
 
    //记录move位置 
    var oldPosition=0 
    var toPosition=0 
 
 
    constructor(context: Context) : super(context) {
   
     
        init(context, null) 
    } 
 
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
   
     
        init(context, attrs) 
    } 
 
    fun init(context: Context?, attrs: AttributeSet?) {
   
     
        if (attrs != null) {
   
    //xml方式调用 
            var ta = context!!.obtainStyledAttributes(attrs, R.styleable.DragRecyclerView) 
            dragEnable = ta.getBoolean(R.styleable.DragRecyclerView_drag_enable, true) 
            showDragAnimation = ta.getBoolean(R.styleable.DragRecyclerView_show_decoration, true) 
        } else {
   
     
            dragEnable = true 
            showDragAnimation = true 
        } 
        mDragItemTouchHelper.attachToRecyclerView(this) 
    } 
 
    //设置是否允许拖动 
    @JvmName("setDragEnable1") 
    fun setDragEnable(b: Boolean) {
   
     
        dragEnable = b 
    } 
 
    //设置是否显示动画 
    @JvmName("setShowDragAnimation1") 
    fun setShowDragAnimation(b: Boolean) {
   
     
        showDragAnimation = b 
    } 
 
    //动画 
    private var startAnimation: ScaleAnimation? = ScaleAnimation( 
        1.0f, 
        1.1f, 
        1.0f, 
        1.1f, 
        Animation.RELATIVE_TO_SELF, 
        0.5f, 
        Animation.RELATIVE_TO_SELF, 
        0.5f 
    ) 
    private var endAnimation: ScaleAnimation? = ScaleAnimation( 
        1.1f, 
        1.0f, 
        1.1f, 
        1.0f, 
        Animation.RELATIVE_TO_SELF, 
        0.5f, 
        Animation.RELATIVE_TO_SELF, 
        0.5f 
    ) 
 
    fun startAnimation(v: View) {
   
     
        v.animation = startAnimation 
        startAnimation!!.fillAfter = true 
        startAnimation!!.duration = 200 
        startAnimation!!.start() 
    } 
 
    fun endAnimation(v: View) {
   
     
        v.animation = endAnimation 
        endAnimation!!.fillAfter = true 
        endAnimation!!.duration = 200 
        endAnimation!!.start() 
    } 
 
 
    fun bindEvent(onItemTouchEvent: HoldTouchHelper.OnItemTouchEvent?): DragRecycleView? {
   
     
        HoldTouchHelper.bind(this, onItemTouchEvent) 
        return this 
    } 
 
 
   var mDragItemTouchHelper =ItemTouchHelper(object : ItemTouchHelper.Callback() {
   
     
 
       /** 
        * 在此方法里面我们需要构建两个flag,一个是dragFlags,表示拖动效果支持的方向, 
        * 另一个是swipeFlags,表示侧滑效果支持的方向。在我们的Demo中,拖动执行上下两个方向, 
        * 侧滑执行左右两个方向,这些操作我们都可以在此方法里面定义。 
        */ 
       override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: ViewHolder): Int {
   
     
           //上下滑动 
           val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN 
           //向左滑动 
           val swipeFlags = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT 
           return makeMovementFlags(dragFlags, swipeFlags) 
       } 
 
       /** 
        * 当拖动效果已经产生了,会回调此方法。在此方法里面,我们通常会更新数据源, 
        * 比如说,一个ItemView从0拖到了1位置,那么对应的数据源也需要更改位置。 
        */ 
       override fun onMove( 
           recyclerView: RecyclerView, 
           viewHolder: ViewHolder, 
           target: ViewHolder 
       ): Boolean {
   
     
 
           oldPosition=viewHolder.adapterPosition 
           toPosition=target.adapterPosition 
//           mItemTouchStatus!!.onItemMove(viewHolder.adapterPosition, target.adapterPosition) 
//           Log.i("DragRecycleView==",mItemTouchStatus!!.onItemMove(viewHolder.adapterPosition, target.adapterPosition).toString()) 
           return  mItemTouchStatus!!.onItemMove(viewHolder.adapterPosition, target.adapterPosition) 
//           return  mItemTouchStatus!!.onItemMove(viewHolder.adapterPosition, target.adapterPosition) 
       } 
 
       /** 
        * 当侧滑效果以上产生了,会回调此方法。在此方法里面,我们也会更新数据源。 
        * 与onMove方法不同到的是,我们在这个方法里面从数据源里面移除相应的数据, 
        * 然后调用notifyXXX方法就行了。 
        */ 
       override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
   
     
       } 
 
       override fun onSelectedChanged(viewHolder: ViewHolder?, actionState: Int) {
   
     
           /** 
            * actionState: 
            * 1.ACTION_STATE_IDLE表示没有任何手势,此时selected对应的应当是null; 
            * 2. ACTION_STATE_SWIPE表示当前ItemView处于侧滑状态; 
            * 3. ACTION_STATE_DRAG表示当前ItemView处于拖动状态。 
            */ 
           if (showDragAnimation && actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
   
     
               startAnimation(viewHolder!!.itemView) 
           } 
           super.onSelectedChanged(viewHolder, actionState) 
       } 
 
       override fun clearView(recyclerView: RecyclerView, viewHolder: ViewHolder) {
   
     
           super.clearView(recyclerView, viewHolder) 
           if (showDragAnimation) {
   
     
               //操作结束后会走这个方法 
               removeView(viewHolder.itemView) 
 
           } 
       } 
 
   }) 
 
    fun setDragAdapter(dragBaseAdapter: ItemTouchStatues?): DragRecycleView? {
   
     
        if (dragBaseAdapter is Adapter<*>) {
   
     
            this.mItemTouchStatus = dragBaseAdapter 
            mDragItemTouchHelper.attachToRecyclerView(this) 
            super.setAdapter(mItemTouchStatus as Adapter<*>?) 
        } else {
   
     
            throw IllegalArgumentException() 
        } 
        return this 
    } 
} 

attrs.xml

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
  <declare-styleable name="DragRecyclerView"> 
        <attr name="show_decoration" format="boolean"/> 
        <attr name="drag_enable" format="boolean"/> 
        <attr name="show_drag_animation" format="boolean"/> 
    </declare-styleable> 
</resources> 

ListActivity .kt

class ListActivity : AppCompatActivity() {
   
     
    var mDataList: List<Bean>? = null 
    private val mLinearLayoutManager: LinearLayoutManager = 
        LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) 
 
    override fun onCreate(savedInstanceState: Bundle?) {
   
     
        super.onCreate(savedInstanceState) 
        setContentView(R.layout.activity_list) 
        init() 
    } 
 
    fun init() {
   
     
        mDataList = getData() 
        var mAdapter = RecyclerViewAdapter(this,mDataList) 
        recycle.setDragAdapter(mAdapter) 
        recycle.layoutManager = mLinearLayoutManager 
    } 
 
 
    fun getData(): List<Bean>? {
   
     
        val dataList: ArrayList<Bean> = ArrayList() 
        for (i in 0 until 20) {
   
     
            var bean = Bean() 
            bean.num = "第" + i + "个" 
            bean.color = 1 
            dataList.add(bean) 
        } 
        return dataList 
    } 
} 

xml:

    <com.light.mytext.mycustomview.list.DragRecycleView 
        android:id="@+id/recycle" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent" /> 

适配器:

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder> implements ItemTouchStatues {
   
     
 
    private List<Bean> mDataList; 
    private Context mContext; 
 
    public RecyclerViewAdapter(Context context,List<Bean> dataList) {
   
     
        mDataList = dataList; 
        mContext=context; 
    } 
 
 
    @NonNull 
    @Override 
    public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
   
     
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false); 
        return new RecyclerViewHolder(view); 
    } 
 
    @Override 
    public void onBindViewHolder(@NonNull final RecyclerViewHolder holder, final int position) {
   
     
        holder.mTextView.setText(mDataList.get(position).getNum()); 
//        holder.itemView.setBackgroundColor(mDataList.get(position).getColor()); 
    } 
 
    @Override 
    public int getItemCount() {
   
     
        return mDataList.size(); 
    } 
 
    @Override 
    public boolean onItemMove(int fromPosition, int toPosition) {
   
     
//        Collections.swap(mDataList, fromPosition, toPosition); 
//        notifyItemMoved(fromPosition, toPosition); 
        if (fromPosition < toPosition) {
   
     
            // after 
            for (int i = fromPosition; i < toPosition; i++) {
   
     
                Collections.swap(mDataList, i, i + 1);//数组中更换两个item的位置 
            } 
        } else {
   
     
            // before 
            for (int i = fromPosition; i > toPosition; i--) {
   
     
                Collections.swap(mDataList, i, i - 1); 
            } 
        } 
        notifyItemMoved(fromPosition, toPosition); 
        ToastUtils.show(mContext,"from"+fromPosition+"to"+toPosition); 
        return true; 
    } 
 
    @Override 
    public boolean onItemRemove(int position) {
   
     
        mDataList.remove(position); 
        notifyItemRemoved(position); 
        return true; 
    } 
 
    public class RecyclerViewHolder extends RecyclerView.ViewHolder {
   
     
 
        TextView mTextView; 
 
        public RecyclerViewHolder(View itemView) {
   
     
            super(itemView); 
            mTextView=(TextView) itemView.findViewById(R.id.tv_text); 
        } 
    } 
} 
 
 

xml:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content"> 
 
    <TextView 
        android:id="@+id/tv_text" 
        android:layout_width="match_parent" 
        android:layout_height="40dp" 
        android:layout_marginBottom="10dp" 
        android:background="#44ff0000" 
        android:gravity="center" 
        android:text="" 
        android:textColor="#ffffff" 
        android:textSize="17sp" /> 
 
</LinearLayout> 

ItemTouchStatues.java

interface ItemTouchStatues {
   
     
    fun onItemMove(fromPosition: Int, toPosition: Int): Boolean 
    fun onItemRemove(position: Int): Boolean 
} 
参考文献

RecyclerView 扩展(二) - 手把手教你认识ItemTouchHelper


评论关闭
IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

安卓自定义View四重叠View(灵感来自头像重叠)