Android 自定义UI 实战 03 RecyclerView 吸顶 仿微信好友列表,最新Android高级面试题汇总

37 阅读1分钟

android:layout_width="match_parent"

android:layout_height="match_parent">

<TextView

android:id="@+id/user_name"

android:text="姓名"

android:gravity="center"

android:layout_width="match_parent"

android:layout_height="44dp"

android:textSize="20sp"

tools:ignore="MissingConstraints" />

RecyclerView 中引用item

<LinearLayout xmlns:android="schemas.android.com/apk/res/and…"

xmlns:app="schemas.android.com/apk/res-aut…"

xmlns:tools="schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context=".MainActivity">

<androidx.recyclerview.widget.RecyclerView

android:id="@+id/main_rv"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:listitem="@layout/item"

/>

2、创建一个 Star


public class Star {

String userName;

String groupName;

public String getUserName() {

return userName;

}

public void setUserName(String userName) {

this.userName = userName;

}

public String getGroupName() {

return groupName;

}

public void setGroupName(String groupName) {

this.groupName = groupName;

}

public Star(String userName, String groupName) {

this.userName = userName;

this.groupName = groupName;

}

}

3、创建一个 StarAdapter


在StarAdapter 中实现 RecyclerView 功能

public class StarAdapter extends RecyclerView.Adapter<StarAdapter.StarViewHolder> {

private Context mContext;

private List starList;

private LayoutInflater inflater;

public StarAdapter(Context context, List stars){

this.mContext = context;

this.starList = stars;

this.inflater = LayoutInflater.from(mContext);

}

@NonNull

@Override

public StarViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

View view = inflater.inflate(R.layout.item, null);

return new StarViewHolder(view);

}

@Override

public void onBindViewHolder(@NonNull StarViewHolder holder, int position) {

holder.textView.setText(starList.get(position).getUserName());

if (position %2 == 0){

holder.textView.setBackgroundColor(Color.YELLOW);

}else {

holder.textView.setBackgroundColor(Color.RED);

}

}

@Override

public int getItemCount() {

return starList.size();

}

public class StarViewHolder extends RecyclerView.ViewHolder {

private TextView textView;

public StarViewHolder(@NonNull View itemView) {

super(itemView);

textView = itemView.findViewById(R.id.user_name);

}

}

}

4、Activity 完整代码


public class MainActivity extends AppCompatActivity {

private RecyclerView recyclerView;

private StarAdapter starAdapter;

private List starList;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

init();

recyclerView = findViewById(R.id.main_rv);

starAdapter = new StarAdapter(this, starList);

recyclerView.setLayoutManager(new LinearLayoutManager(this));

recyclerView.setAdapter(starAdapter);

}

private void init(){

starList = new ArrayList<>();

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

for (int j = 0; j < 20; j++) {

if (i % 2 ==0){

starList.add(new Star("小明" + j, "快乐家族" + i));

}else {

starList.add(new Star("王美丽" + j, "嗨嗨家族" + i));

}

}

}

}

}

上面源码中,我写了一个嵌套的for 循环,用来实现数据的赋值,显示为4组,每组组20条数据。代码运行效果如下:

在这里插入图片描述

二、吸顶功能实现

======================================================================

1、自定义 ItemDecoration


StarDecoration 中,我写了一个分辩率的转换方法

public class StarDecoration extends RecyclerView.ItemDecoration {

/**

  • 分辩率

  • @param context

  • @param dpValue

  • @return

*/

private int dp2px(Context context, float dpValue){

return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue,

context.getResources().getDisplayMetrics());

}

}

2、StarDecoration 初始化


// 分组栏的高度

private int headerHeight;

private int valueHeight = 50;

private Paint headPaint;

private Paint drawTextPaint;

private Rect textRect;

public StarDecoration(Context context){

// 顶部吸顶栏的高度

headerHeight = dp2px(context, valueHeight);

// 每一组的头部的Paint

headPaint = new Paint();

headPaint.setColor(Color.RED);

drawTextPaint = new Paint();

drawTextPaint.setColor(Color.YELLOW);

drawTextPaint.setTextSize(50);

textRect = new Rect();

}

3、重写 getItemOffsets() 方法


ItemDecoration 想要绘制的话,就必须得重写 getItemOffsets() 方法。代码如下

@Override

public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,

@NonNull RecyclerView parent, @NonNull RecyclerView.State state) {

super.getItemOffsets(outRect, view, parent, state);

// 绑定自己的 Adapter

if (parent.getAdapter() instanceof StarAdapter){

StarAdapter starAdapter = (StarAdapter) parent.getAdapter();

// 当前item 的位置

int position = parent.getChildLayoutPosition(view);

// 分割线预留的空间

outRect.set(10, 10, 0, 0);

}

}

上面的代码中,我们给ItemDecoration 预留了一个划线的空间

运行如下:

在这里插入图片描述

4、绘制头部预留空间


  1. 在Adapter 中判断当前组的第一个item 是不是头部

// 是否是组的第一个Item

public boolean isFirstItemOfGroup(int position) {

if (position == 0) {

return true;

} else {

// 拿到当前位置的和前一个位置的 组名

String currentItemGroupName = getGroupName(position);

String preItemGroupName = getGroupName(position - 1);

// 如果相等,则表示position 的 item 不是第一个,否则是

if (currentItemGroupName.equals(preItemGroupName)) {

return false;

} else {

return true;

}

}

}

  1. 在 获取到头部 item 之后,预留一个绘制空间

// 判断 Item 是头部

boolean isGroupHeader = starAdapter.isFirstItemOfGroup(position);

if (isGroupHeader){

// headerHeight 是头部 Item 的高度

outRect.set(0, headerHeight, 0, 0);

}else {

// decorationTop 分割线的高度

outRect.set(0, decorationTop, 0, 0);

}

getItemOffsets() 中添加上面代码后,预留的 item 的头部空间就出来了,运行如下

在这里插入图片描述

5、重写 onDraw() 绘制头部


// 绘制自己

@Override

public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {

super.onDraw(c, parent, state);

if (parent.getAdapter() instanceof StarAdapter){

StarAdapter starAdapter = (StarAdapter) parent.getAdapter();

// 当前屏幕上展示的

int count = parent.getChildCount();

// 实现 itemView 的宽度和分割线的宽度一样

int left = parent.getPaddingLeft();

int right = parent.getWidth() - parent.getPaddingLeft();

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

View view = parent.getChildAt(i);

// 当前item 的位置

int position = parent.getChildLayoutPosition(view);

// 判断是否是头部

boolean isGroupHeader = starAdapter.isFirstItemOfGroup(position);

if (isGroupHeader){

c.drawRect(left, view.getTop() - headerHeight, right, view.getTop(), headPaint);

// 获取当前组名

String groupName = starAdapter.getGroupName(position);

c.drawText(groupName,left + groupNameLeftPadding,

view.getTop() - headerHeight / 2 + textRect.height() / 2,

drawTextPaint);

}else {

c.drawRect(left, view.getTop() - valueHeight, right, view.getTop(), headPaint);

}

}

}

}

代码运行结果如下:

在这里插入图片描述

6、绘制吸顶效果


// 绘制吸顶效果

最后

代码真的是重质不重量,质量高的代码,是当前代码界提倡的,当然写出高质量的代码肯定需要一个相当高的专业素养,这需要在日常的代码书写中逐渐去吸收掌握,谁不是每天都在学习呀,目的还不是为了一个,为实现某个功能写出高质量的代码。

所以,长征路还长,大家还是好好地做个务实的程序员吧。

最后,小编这里有一系列Android提升学习资料,有兴趣的小伙伴们可以来看下哦~

我的 Android 学习,面试文档,视频收集大整理