1、避免创建过多的对象(如点击事件)
2、数据处理与视图绑定分离(onBindViewHolder 应该只是将数据set到视图中,而不应进行业务的处理)
3、数据优化(分页、DiffUtil<数据对比>局部刷新)
4、局部更新(知道哪些position的局部刷新)
notifyItemChanged(int position)
notifyItemInserted(int position)
notifyItemRemoved(int position)
notifyItemMoved(int fromPosition, int toPosition)
notifyItemRangeChanged(int positionStart, int itemCount)
notifyItemRangeInserted(int positionStart, int itemCount)
notifyItemRangeRemoved(int positionStart, int itemCount)
如果必须用 notifyDataSetChanged(),那么最好设置 mAdaptersetHasStableIds(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" />
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
首先是在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方法进行监听
以上就是关于recyclerview全部的内容,包括:recyclerview、再次认识下 RecyclerView setHasFixedSize的方法、Android自定义字母导航栏等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)