如何实现让RecyclerView有不同尺寸的item

如何实现让RecyclerView有不同尺寸的item,第1张

adapter里面计算宽高,这个宽高可以让服务器获取当让我们也可以自己获取。 下面就说下实现方式吧 既然要动态适配宽高就要根据的宽度和手机的宽度计算出比率来然后根据这个比率来计算imageview的高度 package comjtechscrollimageloaddemo; import androidappActivity; import androidcontentContext; import androidviewLayoutInflater; import androidviewView; import androidviewViewGroup; import androidwidgetBaseAdapter; import androidwidgetImageView; import combumptechglideGlide; import comjtechadapterRecyclerAdapter; import comjtechviewRecyclerHolder; import javautilArrayList; import javautilList; / 适配器 关于 同等间距的re

项目地址: >

举一个简单的例子:ImageView的layout_ width="100dp",layout_ height="wrap_ content"的时候,的宽度将会与100dp进行对比(抛开单位换算)。A、如果的宽度小于100dp,ImageView的layout_height将与的高相同,即不会缩放,完整显示在ImageView中,ImageView高度与实际高度相同。没有占满ImageView,ImageView中有空白。B、如果的宽度大于或等于100dp,将保持自身宽高比缩放,完整显示在ImageView中,并且完全占满ImageView。

(三)当ImageView的layout_ width和layout_ height都是wrap_content的时候。adjustViewBounds是没有意义的,因为ImageView将始终与拥有相同的宽高比(但是并不是相同的宽高值,通常都会放大一些)。

dontAnimate()

参考:

>

首先是在RecyclerView的基础上增加了拖动滑动的功能,直接按照官方文档写完如下

ItemTouchHelper(object : ItemTouchHelperCallback() {

override fun getMovementFlags(

recyclerView: RecyclerView,

viewHolder: RecyclerViewViewHolder,

): Int {

//item拖动方向

var dragflag =

ItemTouchHelperUP or ItemTouchHelperDOWN or ItemTouchHelperLEFT or ItemTouchHelperRIGHT

return makeMovementFlags(dragflag, 0)

}

override fun onMove(

recyclerView: RecyclerView,

viewHolder: RecyclerViewViewHolder,

target: RecyclerViewViewHolder,

): Boolean {

myAdapternotifyItemMoved(viewHolderlayoutPosition, targetlayoutPosition)

return true

}

override fun onSwiped(viewHolder: RecyclerViewViewHolder, direction: Int) {}

override fun canDropOver(

recyclerView: RecyclerView,

current: RecyclerViewViewHolder,

target: RecyclerViewViewHolder,

): Boolean {

//当前的ViewHolder可以放在目标的ViewHolder上

return true

}

override fun isLongPressDragEnabled(): Boolean {

//开启长按拖动

return true

}

})attachToRecyclerView(bindingmyRV)//附加到RecyclerView

运行完之后发现UI是正常的,但是获取adapteritems的时候发现数据并没有变动,于是在onMove进行数据交换排序:

ItemTouchHelper(object : ItemTouchHelperCallback() {

override fun getMovementFlags(

recyclerView: RecyclerView,

viewHolder: RecyclerViewViewHolder,

): Int {

//item拖动方向

var dragflag =

ItemTouchHelperUP or ItemTouchHelperDOWN or ItemTouchHelperLEFT or ItemTouchHelperRIGHT

return makeMovementFlags(dragflag, 0)

}

override fun onMove(

recyclerView: RecyclerView,

viewHolder: RecyclerViewViewHolder,

target: RecyclerViewViewHolder,

): Boolean {

if (viewHolderlayoutPosition < targetlayoutPosition) {

for (i in viewHolderlayoutPosition until targetlayoutPosition) {

Collectionsswap(myAdapteritems, i, i + 1) //交换数据源两个数据的位置

}

} else {

for (i in viewHolderlayoutPosition downTo targetlayoutPosition + 1) {

Collectionsswap(myAdapteritems, i, i - 1) //交换数据源两个数据的位置

}

}

myAdapternotifyItemMoved(viewHolderlayoutPosition, targetlayoutPosition)

myAdapternotifyItemChanged(targetlayoutPosition)

return true

}

override fun onSwiped(viewHolder: RecyclerViewViewHolder, direction: Int) {}

override fun canDropOver(

recyclerView: RecyclerView,

current: RecyclerViewViewHolder,

target: RecyclerViewViewHolder,

): Boolean {

//当前的ViewHolder可以放在目标的ViewHolder上

return true

}

override fun isLongPressDragEnabled(): Boolean {

//开启长按拖动

return true

}

})attachToRecyclerView(bindingmyRV)

如果想要监听拖动结束则可以重写clearView方法进行监听

1、首先为了提供运行的速度,我们会对View的 onDraw方法尤其关心,因为会引起垃圾回收,从而导致卡顿,所以在安卓上做动画,请在初始化期间或动画之间分配对象。切勿在动画运行期间进行分配。

2、要降低对 onDraw方法的调用频率,一般 onDraw方法的调用是有 invalidate 引起的 ,所以要避免不必要的调动

3、还有一种超级昂贵的 *** 作便利布局,requestLayout 这个方法如果调用,会对整个界面进行遍历,为了确定每个视图需要的尺寸,而且在遍历过程中,发现了冲突的尺寸, 可能还需要多次遍历层次结构:所以对开发人员来讲 ,少一层 ViewGroup 是必须要考虑的,由于层次结构导致的性能问题,所以浅层次结构的尤其重要

4、对于复杂的界面,使用自定义的ViewGroup

RecycleView的源码注释

在早期的RecycleView的源码中是这样子的

如果 hasFixedSize=true的话, 通过上面的解释,调用 requestLayout是非常昂贵的动作,如果你的 RecycleView 有插入和删除数据,而且是经常的话,这样子是极其可怕的,

所以setHasFixedSize 为 true,是为了更改 adapter的内容不会改变 它的View的高度和宽度,那么就可以设置为 True来避免不必要的 requestLayout

如果我们有一个高度/宽度wrap_content为 as的 RecyclerView, setHasFixedSize应该为 false,因为适配器插入的每个元素都可以根据插入/删除的项目改变大小,因此,每次添加/删除时,大小都会不同项目。RecyclerViewRecyclerView

更清楚地说,如果我们使用固定的宽度/高度

我们可以用my_recycler_viewsetHasFixedSize(true)

那么如果我们不使用固定的宽度/高度

我们应该使用因为my_recycler_viewsetHasFixedSize(false)宽度或高度可以改变我们的大小。wrap_contentRecyclerView

一句话,布局文件 match ,设置 setHasFixedSize =true

自定义侧边字母导航栏,根据实际字母高度进行显示

先上效果图

public class SlideBar extends View {

    //当前手指滑动到的位置

    private int choosedPosition = -1;

    //画文字的画笔

    private Paint paint;

    //单个字母的高度

    private float perTextHeight;

    //字母的字体大小

    private float letterSize;

    //字母的垂直间距

    private float letterGap;

    //字母圆形背景半径

    private float bgRadius;

    private ArrayList<String> firstLetters = new ArrayList<>();

    //绘制点击时的蓝色背景

    private Paint backgroundPaint;

    private Context context;

    private OnTouchFirstListener listener;

    public RecyclerView getTiku_recycle_answer() {

        return tiku_recycle_answer;

    }

    public void setTiku_recycle_answer(RecyclerView tiku_recycle_answer) {

        thistiku_recycle_answer = tiku_recycle_answer;

    }

    RecyclerView tiku_recycle_answer;

    public SlideBar(Context context) {

        this(context, null);

    }

    public SlideBar(Context context, AttributeSet attrs) {

        this(context, attrs, 0);

    }

    public SlideBar(Context context, AttributeSet attrs, int defStyleAttr) {

        super(context, attrs, defStyleAttr);

        thiscontext = context;

        TypedArray typedArray = contextobtainStyledAttributes(attrs, RstyleableSlideBar);

        //字母的字体大小

        letterSize = typedArraygetDimension(RstyleableSlideBar_letter_size, DisplayUtilssp2px(context, 100f));

        //每个字母的高

        perTextHeight = typedArraygetDimension(RstyleableSlideBar_letter_height, DisplayUtilsdp2px(context, 100f));

        //字母垂直间距

        letterGap = typedArraygetDimension(RstyleableSlideBar_letter_gap, DisplayUtilsdp2px(context, 60f));

        //字母垂直间距

        bgRadius = typedArraygetDimension(RstyleableSlideBar_letter_bg_radius, DisplayUtilsdp2px(context, 80f));

        typedArrayrecycle();

        init();

    }

    public void init() {

        //初始化画笔

        paint = new Paint();

        paintsetAntiAlias(true);

        paintsetTextSize(letterSize);

        paintsetTypeface(TypefaceDEFAULT_BOLD);

        //初始化圆形背景画笔

        backgroundPaint = new Paint();

        backgroundPaintsetAntiAlias(true);

        backgroundPaintsetColor(contextgetResources()getColor(Rcolorcolor_368FFF));

    }

    public void setFirstLetters(ArrayList<String> letters) {

        firstLettersclear();

        firstLettersaddAll(letters);

        invalidate();

    }

    @Override

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int widthMode = MeasureSpecgetMode(widthMeasureSpec);  //获取宽的模式

        int heightMode = MeasureSpecgetMode(heightMeasureSpec); //获取高的模式

        int widthSize = MeasureSpecgetSize(widthMeasureSpec);  //获取宽的尺寸

        int heightSize = MeasureSpecgetSize(heightMeasureSpec); //获取高的尺寸

        int width = 0;

        int height;

        if (widthMode == MeasureSpecEXACTLY) {

            //如果match_parent或者具体的值,直接赋值

            width = widthSize;

        } else {

            //如果其他模式,则指定一个宽度

            width = DisplayUtilsdp2px(getContext(), 200f);

        }

        //高度跟宽度处理方式一样

        if (heightMode == MeasureSpecEXACTLY) {

            height = heightSize;

        } else {

            float textHeight = perTextHeight;

            height = (int) (getPaddingTop() + textHeight (firstLetterssize() + 1) + letterGap (firstLetterssize() - 1) + getPaddingBottom());

        }

        if (height > tiku_recycle_answergetMeasuredHeight()) {

            height = tiku_recycle_answergetMeasuredHeight();

        }

        //保存测量宽度和测量高度

        setMeasuredDimension(width, height);

    }

    @Override

    protected void onDraw(Canvas canvas) {

        superonDraw(canvas);

        for (int i = 0; i < firstLetterssize(); i++) {

            paintsetColor(i == choosedPosition ColorWHITE : contextgetResources()getColor(Rcolorcolor_368FFF));

            float x = (getWidth() - paintmeasureText(firstLettersget(i))) / 2;

            float y = (float) getHeight() / firstLetterssize();//每个字母的高度

            if (i == choosedPosition) {

                canvasdrawCircle((float) (getWidth() / 2), i y + y / 2, bgRadius, backgroundPaint);

            }

            //垂直位置需要增加一个偏移量,上移两个像素,因为根据计算得到的是baseline,将字母上移,使其居中在圆内

            canvasdrawText(firstLettersget(i), x, (perTextHeight + y) / 2 + y i-2, paint);

        }

    }

    //触碰事件

    //按下,松开,拖动

    @Override

    public boolean onTouchEvent(MotionEvent event) {

        switch (eventgetAction()) {

            case MotionEventACTION_DOWN:

            case MotionEventACTION_MOVE:

                thissetBackgroundColor(contextgetResources()getColor(androidRcolortransparent));

                float y = eventgetY();

                //获取触摸到字母的位置

                choosedPosition = (int) y firstLetterssize() / getHeight();

                //上滑超过边界,显示第一个

                if (choosedPosition < 0) {

                    choosedPosition = 0;

                }

                //下滑超过边界,显示最后一个

                if (choosedPosition >= firstLetterssize()) {

                    choosedPosition = firstLetterssize() - 1;

                }

                if (listener != null) {

                    //滑动A-Z字母联动外层数据

                    listeneronTouch(firstLettersget(choosedPosition));

                }

                break;

            case MotionEventACTION_UP:

                thissetBackgroundColor(contextgetResources()getColor(androidRcolortransparent));

                choosedPosition = -1;

                if (listener != null) {

                    //滑动A-Z字母联动外层数据

                    listeneronRelease();

                }

                break;

        }

        //重绘

        invalidate();

        return true;

    }

    public void setFirstListener(OnTouchFirstListener listener) {

        thislistener = listener;

    }

    /

    OnTouchFirstListener 接口

    onTouch:触摸到了那个字母

    onRelease:up释放时中间显示的字母需要设置为GONE

    /

    public interface OnTouchFirstListener {

        void onTouch(String firstLetter);

        void onRelease();

    }

}

<declare-styleable name="SlideBar">

    <attr name="letter_size" format="dimension" />

    <attr name="letter_height" format="dimension" />

    <attr name="letter_gap" format="dimension" />

    <attr name="letter_bg_radius" format="dimension" />

</declare-styleable>

xml中引入,我的是constraintlayout,具体设置看自己的布局

<comanswerviewSlideBar

    android:id="@+id/slideBar"

    android:layout_width="@dimen/dp_20"

    android:layout_height="wrap_content"

    app:layout_constraintBottom_toBottomOf="@+id/tiku_recycle_answer"

    app:layout_constraintEnd_toEndOf="parent"

    app:layout_constraintStart_toStartOf="@+id/guide_answer"

    app:layout_constraintTop_toTopOf="@+id/tiku_recycle_answer"

    app:letter_bg_radius="@dimen/dp_8"

    app:letter_gap="@dimen/dp_6"

    app:letter_height="@dimen/dp_10"

    app:letter_size="@dimen/sp_10" />

private void handleSlideBarEvent() {

    List<QuesCommentSubjectiveStuBean> datas = subjectiveCommentDetailAdaptergetDatas();//获取处理后的数据,赋值给导航栏

    ArrayList<String> letters = new ArrayList<>();

    for (QuesCommentSubjectiveStuBean stuBean : datas) {

        if (letterscontains(stuBeangetFirstLetter())) {

            continue;

        }

        lettersadd(stuBeangetFirstLetter());

    }

    slideBarsetFirstLetters(letters);

    slideBarsetTiku_recycle_answer(tiku_recycle_answer);

    slideBarsetFirstListener(new SlideBarOnTouchFirstListener() {

        @Override

        public void onTouch(String firstLetter, float dy) {

            tv_first_lettersetVisibility(VISIBLE);

            tv_first_lettersetText(firstLetter);

            ConstraintLayoutLayoutParams layoutParams = (ConstraintLayoutLayoutParams) tv_first_lettergetLayoutParams();

            //如果是第一个字母,修改提示框显示位置

            layoutParamstopMargin = (int) dy + slideBargetTop() - tv_first_lettergetMeasuredHeight() / 2;

            //异常情况,点击最后一个字符,提示框显示不全的场景,如果显示位置超过屏幕,则靠底部显示

            if ((int) dy + slideBargetTop() + tv_first_lettergetMeasuredHeight() / 2 > tiku_recycle_answergetBottom()) {

                layoutParamstopMargin = tiku_recycle_answergetBottom() - tv_first_lettergetMeasuredHeight();

            }

            tv_first_lettersetLayoutParams(layoutParams);

            //滑动后移动到对应的位置,找到第一个匹配到首字母的学生,位移到此处

            int newPosition = -1;

            for (QuesCommentSubjectiveStuBean stuBean : datas) {

                if (firstLetterequals(stuBeangetFirstLetter())) {

                    newPosition = datasindexOf(stuBean);

                    break;

                }

            }

            //move时会多次触发,此处只响应第一次

            if (newPosition != lastPosition) {

                lastPosition = newPosition;

                Lgd(TAG, "questionComment-->--滑动导航栏跳转到首字母:" + firstLetter);

                subJectLinearLayoutManagerscrollToPositionWithOffset(lastPosition, 0);

            }

        }

        @Override

        public void onRelease() {

            postDelayed(new Runnable() {

                @Override

                public void run() {

                    lastPosition = -1;

                    tv_first_lettersetVisibility(GONE);

                }

            }, 200);

        }

    });

}

5一个小问题。

用于放大显示选中字母的TextView在布局中,请设置为invisible,这样在加载xml布局时,会对这个控件进行测量和布局,只是不显示,这样我们才能获得tv_first_lettergetMeasuredHeight(),如果设置为gone,不会进行测量,获取的高度就为0,这样在第一次显示的时候就会有一个显示位置跳动的异常。设置为invisible就可以解决这个问题,目的就是让系统测量一下TextView的宽高,不想这么搞的话,在第4步之前手动测量一次也是可以的。

<TextView

    android:id="@+id/tv_first_letter"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_marginEnd="@dimen/dp_2"

    android:background="@mipmap/ic_bubble"

    android:fontFamily="sans-serif"

    android:gravity="center"

    android:text="C"

    android:textColor="@color/color_ffffff"

    android:textSize="@dimen/sp_18"

    android:visibility="invisible"

    app:layout_constraintEnd_toStartOf="@+id/guide_answer"

    app:layout_constraintTop_toTopOf="parent" />

该方法只适用于item高度固定,在本例中使用34dp来设置相应的item高度,故而可以通过乘上相应的item数来计算RecyclerView的高度。

在使用这种方式时,有一点需要注意的是,不要将RecyclerView的android:layout_height属性设置为wrap_content,不然是不会成功的。

方法中的recycler是一个item的循环使用器,起到对item管理的作用。

源码链接

效果图

实现步骤:

1在buildgradle文件中加入recyclerView库

2在mainxml中使用recyclerView布局

3滚动列表中的每一项对应一个对象,他们属于同一类的实例。接着定义实体类,我做的是歌单列表,定义序号,名字和时长

4设计列表项的样式

4使用适配器为列表项设置显示的数据

41定义适配器的内部类与列表项绑定

42适配器继承自recyclerViewAdapter,要重写三个方法,创建列表项视图,复用加载,获取列表项数量,到这一步,循环列表已经可以实现滚动效果了。

5为列表项添加点击监听事件,使用接口回调

51仍然是在适配器中,定义接口

52在onBindViewHolder中为列表项设置监听

53在MainActivity中使用,实现onclick方法

以上就是关于如何实现让RecyclerView有不同尺寸的item全部的内容,包括:如何实现让RecyclerView有不同尺寸的item、XRecyclerView、Recyclerview中Item的ImageView高度wrapcontent使用Glide加载需要注意的地方等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/9464989.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-28
下一篇2023-04-28

发表评论

登录后才能评论

评论列表(0条)

    保存