RecyclerView仿GridView实现一个可以拖动Item删除效果

安卓开发中,我们知道网格布局使用GirdLayout,实现网格布局的组件是GridView。现在项目需要实现如下图的一个功能:
在这里插入图片描述
中间的图片显示我们可以设计3中实现方式:

1)使用GridLayout添加ItemView的方式
2)使用GridViewx控件,通过Adapter来控制显示的界面
3)使用RecyclerViewk控件,通过设置GridLayoutManager来设置Item的显示方式。

但是根据需求,需要让图片Item可以拖动到底部删除,这个功能我们会发现使用方法1会比较麻烦,因为添加图片的方式有多种,删除图片的方式也有多种,添加、删除item的方法不好控制,最重要的一点就是不够简单,复杂就代表bug多。
然后我们来说第二种和第三种方式,其实区别不大,重要的是GridView的Adapter提供了一个setOnItemLongClickListener来设置Item长按的事件,但是使用RecyclerView就需要在Adapter里面通过控件的Id来设置长按事件,长按可以拖动方法主要就是需要获取到长按控件的一个快照,然后对快照进行操作。

  //通过被长按item,获取拖动item的bitmap
            Bitmap dragBitmap = Bitmap.createBitmap(view.getDrawingCache());

下面是一个实现了可以拖拽的GridView自定义控件代码,只需要自己给它设置数据之后就可以执行item的拖拽事件了。

package com.example.xiaolin.draggridview.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.ImageView;

import com.blankj.utilcode.util.LogUtils;
import com.example.xiaolin.draggridview.adapter.GridViewAdapter;

public class DragGridView extends GridView {
    private static final int DRAG_IMG_SHOW = 1;
    private static final int DRAG_IMG_NOT_SHOW = 0;
    private static final String LOG_TAG = "DragGridView";
    private static final float AMP_FACTOR = 1.2f;

    private ImageView dragImageView;
    private WindowManager.LayoutParams dragImageViewParams;
    private WindowManager windowManager;
    private boolean isViewOnDrag = false;

    /**previous dragged over position*/
    private int preDraggedOverPositon = AdapterView.INVALID_POSITION;
    private int downRawX;
    private int downRawY;
    private OnItemLongClickListener onLongClickListener = new OnItemLongClickListener(){

        @Override
        //长按item开始拖动
        public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {

            //记录长按item位置
            preDraggedOverPositon = position;

            //获取被长按item的drawing cache
            view.destroyDrawingCache();
            view.setDrawingCacheEnabled(true);
            //通过被长按item,获取拖动item的bitmap
            Bitmap dragBitmap = Bitmap.createBitmap(view.getDrawingCache());

            //设置拖动item的参数
            dragImageViewParams.gravity = Gravity.TOP | Gravity.LEFT;
            //设置拖动item为原item 1.2倍
            dragImageViewParams.width = (int)(AMP_FACTOR*dragBitmap.getWidth());
            dragImageViewParams.height = (int)(AMP_FACTOR*dragBitmap.getHeight());
            //设置触摸点为绘制拖动item的中心
            dragImageViewParams.x = (downRawX - dragImageViewParams.width/2);
            dragImageViewParams.y = (downRawY - dragImageViewParams.height/2);
            dragImageViewParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                                        | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
            dragImageViewParams.format = PixelFormat.TRANSLUCENT;
            dragImageViewParams.windowAnimations = 0;

            //dragImageView为被拖动item的容器,清空上一次的显示
            if((int)dragImageView.getTag() == DRAG_IMG_SHOW) {
                windowManager.removeView(dragImageView);
                dragImageView.setTag(DRAG_IMG_NOT_SHOW);
            }

            //设置本次被长按的item
            dragImageView.setImageBitmap(dragBitmap);

            //添加拖动item到屏幕
            windowManager.addView(dragImageView, dragImageViewParams);
            dragImageView.setTag(DRAG_IMG_SHOW);
            isViewOnDrag = true;

            //设置被长按item不显示
            ((GridViewAdapter)getAdapter()).hideView(position);
            return true;
        }
    };

    public DragGridView(Context context) {
        super(context);
        initView();
    }

    public DragGridView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public DragGridView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    public void initView() {
        setOnItemLongClickListener(onLongClickListener);
        //初始化显示被拖动item的image view
        dragImageView = new ImageView(getContext());
        dragImageView.setTag(DRAG_IMG_NOT_SHOW);
        //初始化用于设置dragImageView的参数对象
        dragImageViewParams = new WindowManager.LayoutParams();
        //获取窗口管理对象,用于后面向窗口中添加dragImageView
        windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    }


    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        //被按下时记录按下的坐标
        if(ev.getAction() == MotionEvent.ACTION_DOWN) {
            //获取触摸点相对于屏幕的坐标
            downRawX = (int)ev.getRawX();
            downRawY = (int)ev.getRawY();
        }
        //dragImageView处于被拖动时,更新dragImageView位置
        else if((ev.getAction() == MotionEvent.ACTION_MOVE) && (isViewOnDrag == true)) {
            Log.i(LOG_TAG, "" + ev.getRawX() + " " + ev.getRawY());
            //设置触摸点为dragImageView中心
            dragImageViewParams.x = (int)(ev.getRawX() - dragImageView.getWidth()/2);
            dragImageViewParams.y = (int)(ev.getRawY() - dragImageView.getHeight()/2);
            //更新窗口显示
            windowManager.updateViewLayout(dragImageView, dragImageViewParams);
            //获取当前触摸点的item position
            int currDraggedPosition = pointToPosition((int)ev.getX(), (int)ev.getY());
            //如果当前停留位置item不等于上次停留位置的item,交换本次和上次停留的item
            if((currDraggedPosition != AdapterView.INVALID_POSITION) && (currDraggedPosition != preDraggedOverPositon)) {
                ((GridViewAdapter)getAdapter()).swapView(preDraggedOverPositon, currDraggedPosition);
                preDraggedOverPositon = currDraggedPosition;
            }
        }
        //释放dragImageView
        else if((ev.getAction() == MotionEvent.ACTION_UP) && (isViewOnDrag == true)) {
            ((GridViewAdapter)getAdapter()).showHideView();
            if((int)dragImageView.getTag() == DRAG_IMG_SHOW) {
                windowManager.removeView(dragImageView);
                ((GridViewAdapter)getAdapter()).removeView(dragImageView.getId());
                dragImageView.setTag(DRAG_IMG_NOT_SHOW);
            }
            isViewOnDrag = false;
        }
        return super.onTouchEvent(ev);
    }
}

但是我们发现,其实我们并不需要实现图片之间的位置互换功能,只需要实现图片Item拖拽到底部进行删除的功能,那么就不需要更改Item的位置。而且RecyclerView有一个ItemTouchHelper帮助类来实现图片的手势的操作,实现方式简单,所以最后我们选用RecyclerView设置GridLayoutManager的方式来实现这个功能。

class FullyGridLayoutManager : GridLayoutManager {

    private val mMeasuredDimension = IntArray(2)

    internal val mState = RecyclerView.State()

    constructor(context: Context, spanCount: Int) : super(context, spanCount) {}

    constructor(context: Context, spanCount: Int, orientation: Int, reverseLayout: Boolean) : super(context, spanCount, orientation, reverseLayout) {}

    override fun onMeasure(recycler: RecyclerView.Recycler?, state: RecyclerView.State?, widthSpec: Int, heightSpec: Int) {
        val widthMode = View.MeasureSpec.getMode(widthSpec)
        val heightMode = View.MeasureSpec.getMode(heightSpec)
        val widthSize = View.MeasureSpec.getSize(widthSpec)
        val heightSize = View.MeasureSpec.getSize(heightSpec)

        var width = 0
        var height = 0
        val count = itemCount
        val span = spanCount
        for (i in 0 until count) {
            measureScrapChild(recycler, i,
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    mMeasuredDimension)

            if (orientation == LinearLayoutManager.HORIZONTAL) {
                if (i % span == 0) {
                    width += mMeasuredDimension[0]
                }
                if (i == 0) {
                    height = mMeasuredDimension[1]
                }
            } else {
                if (i % span == 0) {
                    height += mMeasuredDimension[1]
                }
                if (i == 0) {
                    width = mMeasuredDimension[0]
                }
            }
        }

        when (widthMode) {
            View.MeasureSpec.EXACTLY -> width = widthSize
        }

        when (heightMode) {
            View.MeasureSpec.EXACTLY -> height = heightSize
        }

        setMeasuredDimension(width, height)
    }

    private fun measureScrapChild(recycler: RecyclerView.Recycler?, position: Int, widthSpec: Int,
                                  heightSpec: Int, measuredDimension: IntArray) {
        val itemCount = mState.itemCount
        if (position < itemCount) {
            try {
                val view = recycler!!.getViewForPosition(0)
                if (view != null) {
                    val p = view.layoutParams as RecyclerView.LayoutParams
                    val childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
                            paddingLeft + paddingRight, p.width)
                    val childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                            paddingTop + paddingBottom, p.height)
                    view.measure(childWidthSpec, childHeightSpec)
                    measuredDimension[0] = view.measuredWidth + p.leftMargin + p.rightMargin
                    measuredDimension[1] = view.measuredHeight + p.bottomMargin + p.topMargin
                    recycler.recycleView(view)
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

RecyclerView的手势帮助类,最主要的是它的回调方法,我们需要实现ItemTouchHelper.Callback接口的方法来实现拖拽功能。

/**
 * ItemTouchHelper 回调接口
 */
public class TouchHelperCallback extends ItemTouchHelper.Callback {

    public static final float ALPHA_FULL = 1.0f;
    private ConstraintLayout mDeletePicLayout;
    private OnPictureItemDeleteListener mItemDeleteListener;

    public TouchHelperCallback(ConstraintLayout deletePicLayout) {
        mDeletePicLayout = deletePicLayout;
    }

    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        return true;
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
            final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
            final int swipeFlags = 0;
            return makeMovementFlags(dragFlags, swipeFlags);
        } else {
            final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
            return makeMovementFlags(dragFlags, swipeFlags);
        }
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
        if (source.getItemViewType() != target.getItemViewType()) {
            return false;
        }
        return true;
    }

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

    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
            // Fade out the view as it is swiped out of the parent's bounds
            final float alpha = ALPHA_FULL - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
            viewHolder.itemView.setAlpha(alpha);
            viewHolder.itemView.setTranslationX(dX);
        } else {
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        }
        if(isCurrentlyActive){
            mDeletePicLayout.setVisibility(View.VISIBLE);
            int itemViewBottom = viewHolder.itemView.getBottom();
            int itemViewTop = viewHolder.itemView.getTop();
            float itemViewY = viewHolder.itemView.getY();
            float deletePicLayoutTop = mDeletePicLayout.getY();
            int adapterPosition = viewHolder.getAdapterPosition();
            if(adapterPosition <= 1){
                if((itemViewBottom+ itemViewY) >= deletePicLayoutTop){
                    mItemDeleteListener.onItemDeleted(viewHolder.getAdapterPosition());
                }
            }else {
                if((itemViewTop+ itemViewY) >= deletePicLayoutTop){
                    mItemDeleteListener.onItemDeleted(viewHolder.getAdapterPosition());
                }
            }
        }else{
            mDeletePicLayout.setVisibility(View.GONE);
        }
    }

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        // We only want the active item to change
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {

        }
        super.onSelectedChanged(viewHolder, actionState);
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);

        viewHolder.itemView.setAlpha(ALPHA_FULL);
    }
    
    public void setOnPictureItemDeleteListener(OnPictureItemDeleteListener itemDeleteListener){
           mItemDeleteListener = itemDeleteListener;
    }
    public interface OnPictureItemDeleteListener{
         void onItemDeleted(int deletePosition);
    }
}

最后就是需要给Recyclerview设置LayoutManager成我们自定义的GridLayouManager,然后把ItemToucherHelper设置给Adaptert的ViewHoder进行数据绑定。

  val manager = FullyGridLayoutManager(this@HomeCreateWriteWorkActivity, 2, GridLayoutManager.VERTICAL, false)
        mPictureGrideView.layoutManager = manager
        val helperCallback = TouchHelperCallback(mDeletePicLayout)
        val mItemDragHelper = ItemTouchHelper(helperCallback)
        mImageAdapter = HomeGridImageAdapter(this@HomeCreateWriteWorkActivity, onAddPicClickListener, mItemDragHelper, helperCallback)

最后最重要的就是需要把手势处理事件添加到我们的RecyclerView:

   mItemDragHelper.attachToRecyclerView(mPictureGrideView)

这样我们就可以实现图片的拖拽的功能了。
但是需要注意的就是我们这里需要有图片添加的功能,添加图片使用的也是第三方的开源框架,下面我就直接把Activity的代码贴出来:

class HomeCreateWriteWorkActivity : AbsTitleFenJiActivity() {

    private var mMaxSelectedNum = 4
    private var mCurrentSelectedNum = 0
    private var mQuestionId: String? = null
    private lateinit var mPictureGrideView: RecyclerView
    private lateinit var mLlPostLoadingView: LinearLayout
    private lateinit var mDeletePicLayout: ConstraintLayout
    private var mImageAdapter: HomeGridImageAdapter? = null
    private var selectList: ArrayList<LocalMedia> = ArrayList()
    private var mPictureList: ArrayList<String> = ArrayList()


    override fun getTitleString(): String {
        return "写一写"
    }

    override fun getTitleRightString(): String {
        return ""
    }

    override fun getTitleRightImageView(): Int {
        return 0
    }

    override fun getLayoutId(): Int {
        return R.layout.activity_create_write_work
    }

    override fun initViews(savedInstanceState: Bundle?) {
        super.initViews(savedInstanceState)
        setImmersionBar(R.color.trans, true, true)
        mPictureGrideView = findView(R.id.gl_picture_recyclerview)
        mDeletePicLayout = findView(R.id.cl_delete_pic_layout)
        mLlPostLoadingView = findView(R.id.ll_layout_paying)
    }

    override fun initData(savedInstanceState: Bundle?) {
        val bundle = intent.extras
        if (ObjectUtils.isNotEmpty(bundle)) {
            mQuestionId = bundle.getString(ConstantExtra.WRITE_QUESTION_ID)
        }

        val createTipsTv: AppCompatTextView = findView(R.id.tv_create_write_tips)
        createTipsTv.text = "提示:请上传回答课后问题的图片,并确保图片清晰、\n" +
                "字迹易辨认,老师点评后会消息及时通知,请留意微\n" +
                "信消息。"

        val manager = FullyGridLayoutManager(this@HomeCreateWriteWorkActivity, 2, GridLayoutManager.VERTICAL, false)
        mPictureGrideView.layoutManager = manager
        val helperCallback = TouchHelperCallback(mDeletePicLayout)
        val mItemDragHelper = ItemTouchHelper(helperCallback)
        mImageAdapter = HomeGridImageAdapter(this@HomeCreateWriteWorkActivity, onAddPicClickListener, mItemDragHelper, helperCallback)
        mImageAdapter?.setList(selectList)
        mImageAdapter?.setSelectMax(mMaxSelectedNum)
        mPictureGrideView.adapter = mImageAdapter

        mItemDragHelper.attachToRecyclerView(mPictureGrideView)
        mImageAdapter?.setOnItemClickListener(object : HomeGridImageAdapter.OnItemClickListener {
            override fun onItemClick(position: Int, v: View) {
                if (selectList.size > 0) {
                    val media = selectList.get(position)
                    val pictureType = media.pictureType
                    val mediaType = PictureMimeType.pictureToVideo(pictureType)
                    when (mediaType) {
                        1 ->
                            PictureSelector.create(this@HomeCreateWriteWorkActivity).themeStyle(R.style.picture_default_style).openExternalPreview(position, selectList)
                    }
                }
            }
        })
    }

    @SuppressLint("ImplicitSamInstance")
    override fun initListeners() {
        mHeadBackBtn.setOnClickListener { sureExitPublishWriteWorkActivity() }
        val rlpostWritePicture = findView<RelativeLayout>(R.id.rl_post_write_pitures)
        rlpostWritePicture.setOnClickListener {
            mLlPostLoadingView.visibility = View.VISIBLE
            ViewClickUtils.setViewDelaySecondsClickAble(rlpostWritePicture, mCurrentSelectedNum * 500)
            mLlPostLoadingView.postDelayed({
                if (mCurrentSelectedNum > 0 && mPictureList.size > 0 && mCurrentSelectedNum == mPictureList.size) {
                    postWriteWorkPictureList()
                }
            }, mCurrentSelectedNum * 500L) //延迟上传图片
        }
        mImageAdapter?.setOnAddPicDelectListener(object : HomeGridImageAdapter.OnAddPicDelectListener {
            override fun onAddPicDelete(listSize: Int) {
                if (listSize < 4) {
                    mMaxSelectedNum = 4 - selectList.size
                }
                mCurrentSelectedNum = listSize
            }
        })
    }

    /**
     * 屏蔽物理返回按钮
     */
    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        sureExitPublishWriteWorkActivity()
        return keyCode == KeyEvent.KEYCODE_BACK
    }

    private fun sureExitPublishWriteWorkActivity() {
        val tipMessageDialog = TipMessageDialog.Builder(context)
        tipMessageDialog.setDialogBackground(R.drawable.bg_white_four_corners_r10)
        tipMessageDialog.setMessage("您确定跳出该页面吗?")
        tipMessageDialog.setMessageTextSize(16)
        tipMessageDialog.setButtonTextAndListener("再想想", "确认", object : TipMessageDialog.OnButtonClickListener {
            override fun onPositiveBtnClick() {

            }

            override fun onNegativeBtnClick() {
                finish()
            }
        })
        tipMessageDialog.createTipPopToShow(false)
    }

    private fun postWriteWorkPictureList() {
        val netController = object : NetController<Any>(mDisposable) {
            override fun success(result: Response<Any>?) {
                mLlPostLoadingView.visibility = View.GONE
                if (ObjectUtils.isNotEmpty(result)) {
                    finish()
                }
            }

            override fun failure(throwable: Throwable?) {
                super.failure(throwable)
                mLlPostLoadingView.visibility = View.GONE
                showCentreToastByText("上传失败,请重试")
            }
        }
        val selectedPicList = ArrayList<String>()
        for (index in 0 until mCurrentSelectedNum) {
            selectedPicList.add(mPictureList[index])
        }
        if (ObjectUtils.isNotEmpty(mQuestionId)) {
            netController.request(StudentApi.getService().postWriteWorkList(mQuestionId?.toLong()!!, selectedPicList.toString()))
        }
    }

    override fun isSwipeBackAble(): Boolean {
        return true
    }

    override fun isBindEventBus(): Boolean {
        return false
    }

    private val onAddPicClickListener = object : HomeGridImageAdapter.onAddPicClickListener {
        override fun onAddPicClick() {
            // 进入相册 以下是例子:不需要的api可以不写
            PictureSelector.create(this@HomeCreateWriteWorkActivity)
                    .openGallery(PictureMimeType.ofImage())// 全部.PictureMimeType.ofAll()、图片.ofImage()、视频.ofVideo()、音频.ofAudio()
                    .theme(R.style.picture_default_style)// 主题样式设置 具体参考 values/styles   用法:R.style.picture.white.style
                    .maxSelectNum(mMaxSelectedNum)// 最大图片选择数量
                    .minSelectNum(1)// 最小选择数量
                    .imageSpanCount(4)// 每行显示个数
                    .selectionMode(PictureConfig.MULTIPLE)// 多选 or 单选
                    .previewImage(false)// 是否可预览图片
                    .isCamera(true)// 是否显示拍照按钮
                    .isZoomAnim(true)// 图片列表点击 缩放效果 默认true
                    //.enableCrop(true)// 是否裁剪
                    .compress(true)// 是否压缩
                    .cropCompressQuality(70)
                    .compressSavePath(getBaseContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString())
                    .synOrAsy(true)//同步true或异步false 压缩 默认同步
                    .glideOverride(160, 160)// glide 加载宽高,越小图片列表越流畅,但会影响列表图片浏览的清晰度
                    .withAspectRatio(1, 1)// 裁剪比例 如16:9 3:2 3:4 1:1 可自定义
                    .hideBottomControls(true)// 是否显示uCrop工具栏,默认不显示
                    .isGif(false)// 是否显示gif图片
                    .freeStyleCropEnabled(false)// 裁剪框是否可拖拽
                    .circleDimmedLayer(false)// 是否圆形裁剪
                    .showCropFrame(false)// 是否显示裁剪矩形边框 圆形裁剪时建议设为false
                    .showCropGrid(true)// 是否显示裁剪矩形网格 圆形裁剪时建议设为false
                    .openClickSound(false)// 是否开启点击声音
                    .minimumCompressSize(100)// 小于100kb的图片不压缩
                    .forResult(PictureConfig.CHOOSE_REQUEST)//结果回调onActivityResult code
        }

    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        try {
            if (resultCode == Activity.RESULT_OK) {
                when (requestCode) {
                    PictureConfig.CHOOSE_REQUEST -> {
                        // 图片选择结果回调
                        val selectPictureList = PictureSelector.obtainMultipleResult(data) as ArrayList<LocalMedia>
                        selectList.addAll(selectPictureList)
                        mImageAdapter?.setList(selectList)
                        mImageAdapter?.setSelectMax(4)
                        mImageAdapter?.notifyDataSetChanged()
                        var headerUrl = ""
                        if (selectList.size <= 4) {
                            mMaxSelectedNum = 4 - selectList.size
                            mCurrentSelectedNum = selectList.size
                            for (index in 0 until selectList.size) {
                                val media = selectPictureList[index]
                                val compressPath = media.compressPath
                                val uuid = TokenManager.getInstance().uuid
                                val time = System.currentTimeMillis()
                                val key: String
                                key = if (ObjectUtils.isNotEmpty(uuid)) {
                                    "avatar/" + uuid + "_" + time.toString() + index
                                } else {
                                    "avatar/$time$index"
                                }
                                QiNiuUploader.newInstance().uploadPhoto(compressPath, key, object : UploadChangeListener {
                                    override fun complete(key: String, status: Boolean, info: String) {
                                        headerUrl = ConstantConfig.QINIU_HOST + key
                                        mPictureList.add("\"" + headerUrl + "\"")
                                        LogUtils.e(">>>>>>>>>>>>>>>>>", "图片上传七牛成功headerUrl:$headerUrl")
                                    }

                                    override fun progress(key: String, percent: Double) {

                                    }
                                })
                            }
                        }
                    }
                }
            }
        } catch (e: Exception) {
            LogUtils.e(">>>>>>>>>>>>>>", "选择相册图片报错:$e")
        }
    }
}

这里最重要的需要注意我们的布局设置了,因为如果我们设置RecyclerView的高度成固定高度的话,那么我们图片Item的拖拽距离就固定在RecyclerView区域,无法实现功能上的拖拽到底部布局,这样就是不符合功能需求的。那我们该怎么解决呢?
其实我们会发现其实解决办法就是使用FrameLayout设置外层布局,然后让RecyclerView填充整个父窗体,背景设置成透明,然后在设置布局的其他元素,当然如果控件有点击事件,则需要设置在上层。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
 <include
     android:id="@+id/title_layout"
     layout="@layout/title_view"
     app:layout_constraintTop_toTopOf="parent"
     app:layout_constraintLeft_toLeftOf="parent"/>
  <View
      android:layout_width="match_parent"
      android:layout_height="@dimen/dp_1"
      android:background="@color/color_f4"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/title_layout"/>
   <FrameLayout
       android:layout_width="match_parent"
       android:layout_height="0dp"
       android:layout_weight="1">

       <android.support.constraint.ConstraintLayout
           android:id="@+id/cl_delete_pic_layout"
           android:layout_width="match_parent"
           android:layout_height="@dimen/dp_61"
           android:background="#E95454"
           android:layout_gravity="bottom"
           android:visibility="invisible"
           app:layout_constraintLeft_toLeftOf="parent"
           app:layout_constraintRight_toRightOf="parent"
           app:layout_constraintBottom_toBottomOf="parent">
           <android.support.v7.widget.AppCompatImageView
               android:layout_width="@dimen/dp_17"
               android:layout_height="@dimen/dp_16"
               android:src="@drawable/ic_white_crash_bucket"
               android:layout_marginTop="@dimen/dp_10"
               app:layout_constraintTop_toTopOf="parent"
               app:layout_constraintRight_toRightOf="parent"
               app:layout_constraintLeft_toLeftOf="parent"/>
           <android.support.v7.widget.AppCompatTextView
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:layout_marginTop="@dimen/dp_35"
               android:text="拖动到此处删除"
               android:textColor="@color/white"
               android:textSize="@dimen/dp_12"
               app:layout_constraintTop_toTopOf="parent"
               app:layout_constraintRight_toRightOf="parent"
               app:layout_constraintLeft_toLeftOf="parent"/>
       </android.support.constraint.ConstraintLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp_1"
        android:background="@color/color_f4"
        android:layout_marginLeft="@dimen/dp_24"
        android:layout_marginRight="@dimen/dp_28"
        android:layout_marginTop="@dimen/dp_49"/>
    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/tv_create_write_tips"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="提示:请上传回答课后问题的图片,并确保图片清晰、
字迹易辨认,老师点评后会消息及时通知,请留意微信消息。"
        android:textColor="#ffa3a3a3"
        android:textSize="@dimen/dp_13"
        android:layout_marginLeft="@dimen/dp_24"
        android:layout_marginRight="@dimen/dp_24"
        android:layout_marginTop="@dimen/dp_49"
        android:layout_gravity="bottom"
        android:layout_marginBottom="@dimen/dp_180" />

       <android.support.v7.widget.RecyclerView
           android:id="@+id/gl_picture_recyclerview"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:paddingLeft="@dimen/dp_33"
           android:paddingRight="@dimen/dp_38"
           android:layout_marginTop="@dimen/dp_19"
           android:background="@color/trans"
           />
       <RelativeLayout
           android:id="@+id/rl_post_write_pitures"
           android:layout_width="@dimen/dp_80"
           android:layout_height="@dimen/dp_70"
           android:layout_gravity="bottom|right"
           android:layout_marginBottom="@dimen/dp_100"
           app:layout_constraintRight_toRightOf="parent"
           app:layout_constraintTop_toBottomOf="@+id/tv_create_write_tips">
           <android.support.v7.widget.AppCompatTextView
               android:layout_width="@dimen/dp_52"
               android:layout_height="@dimen/dp_29"
               android:text="发表"
               android:textColor="@color/white"
               android:textSize="@dimen/dp_15"
               android:gravity="center"
               android:layout_alignParentRight="true"
               android:layout_marginTop="@dimen/dp_10"
               android:layout_marginRight="@dimen/dp_19"
               android:background="@drawable/bg_green_9c_solid_r0"
               />
       </RelativeLayout>

       <LinearLayout
           android:id="@+id/ll_layout_paying"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_gravity="center"
           android:visibility="gone"
           android:orientation="vertical">
           <ProgressBar
               android:layout_width="50dp"
               android:layout_height="50dp"
               android:layout_gravity="center"
               android:indeterminateDrawable="@drawable/bg_loading_progress"
               android:indeterminateBehavior="repeat"/>
           <android.support.v7.widget.AppCompatTextView
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:layout_marginTop="@dimen/dp_5"
               android:textSize="@dimen/sp_17"
               android:textColor="@color/black"
               android:layout_gravity="center"
               android:text="正在上传..."/>
       </LinearLayout>
   </FrameLayout>
</LinearLayout>

这样我们的功能就可以实现了,但是也存在一些小问题,不如拖拽的界面可能会显示在底层,上层被有点击事件的其他控件遮挡,还有就是RecyclerView的边距无法拖拽显示出来。
解决办法就是动态代码设置一显示控件的位置。
下面就是开源项目的传送门链接: DragGridView-master

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RecyclerView是Android中非常强大的控件,它允许我们以列表的形式展示数据。但是,有时候我们需要以网格的形式展示数据,这时候我们就需要使用RecyclerViewGridView模式。 GridView模式允许我们以网格的形式展示数据,类似于Android中GridView控件的效果GridView模式的实现方式和普通的RecyclerView有些不同,需要使用GridLayoutManager来实现。 下面是一个简单的例子,演示如何在RecyclerView中使用GridView模式: 1. 首先,在gradle文件中添加RecyclerView依赖: ``` implementation 'androidx.recyclerview:recyclerview:1.1.0' ``` 2. 在布局文件中添加RecyclerView控件: ``` <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp" app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" app:spanCount="2" /> ``` 注意,这里的layoutManager需要使用GridLayoutManager,并且需要设置spanCount属性,表示每行或每列的网格数量。 3. 创建适配器Adapter,继承自RecyclerView.Adapter: ``` class MyAdapter(private val dataList: List<String>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() { class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val textView: TextView = itemView.findViewById(R.id.textView) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false) return ViewHolder(itemView) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val data = dataList[position] holder.textView.text = data } override fun getItemCount() = dataList.size } ``` 这里的ViewHolder中只包含一个TextView,用于展示数据。在onCreateViewHolder中创建ViewHolder实例,onBindViewHolder中将数据绑定到ViewHolder中。 4. 在Activity或Fragment中设置RecyclerView的适配器: ``` val dataList = listOf("A", "B", "C", "D", "E", "F", "G", "H", "I", "J") val recyclerView = findViewById<RecyclerView>(R.id.recyclerView) recyclerView.adapter = MyAdapter(dataList) ``` 这里的dataList是要展示的数据,可以是任意类型的列表。将数据列表传递给Adapter,然后设置RecyclerView中。 这样,就可以在RecyclerView中展示GridView模式的数据了。如果需要修改每行或每列的网格数量,只需要修改spanCount属性即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值