(代码过长,可以到http://blog.houxinlin.com/project/android/dingding.tar.gz进行下载)
一、效果图
二、实现过程
首先定义数据信息类,包括基本的姓名,头像,加入时间。家长和学生扩展自BaseInfo
学生信息定义如下,并扩展了一个家长集合。
家长信息定义如下,其中扩展了和学生的关系名称,并持有一份学生的信息。
MainActivity.java
这里的学生和家长信息使用Android提供的ArrayMap保存,key是单个字母,value是一堆StudentEntity集合,而StudentEntity中又包含一堆StudentFamily集合,存放多个家长信息。
首先是把所有学生的姓名拼音首字母提取出来,然后存放到ArrayMap中(ArrayMap可以通过下标进行访问元素)。最终形成单个字母对应多个学生,单个学生又对应多个家长信息的结构。
提取拼音可以使用com.belerweb:pinyin4这个框架。
implementation 'com.belerweb:pinyin4j:2.5.0'
import android.os.Bundle;
import android.util.ArrayMap;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.hxl.gongzhonghaodemo.dingding.adapter.StudentAdapter;
import com.hxl.gongzhonghaodemo.dingding.entitys.StudentEntity;
import com.hxl.gongzhonghaodemo.dingding.entitys.StudentFamily;
import net.sourceforge.pinyin4j.PinyinHelper;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private RecyclerView mRecyclerView;
private StudentAdapter mStudentAdapter;
private ArrayMap
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
mStudentAdapter = new StudentAdapter(this, mStudentMap);
mRecyclerView.setAdapter(mStudentAdapter);
}
private void initData() {
mStudentMap = new ArrayMap<>();
List
mStudentEntities.add(new StudentEntity.Builder()
.setName("鲁迅")
.setUrl("http://img5.imgtn.bdimg.com/it/u=3841760432,773912449&fm=11&gp=0.jpg")
.addFamily(new StudentFamily("鲁豫", "", LocalDateTime.now(), "陌生人"))
.addFamily(new StudentFamily("鲁智深", "", LocalDateTime.now(), "陌生人"))
.build());
mStudentEntities.add(new StudentEntity.Builder()
.setName("孔子")
.setUrl("http://img2.imgtn.bdimg.com/it/u=3927250071,3411280749&fm=26&gp=0.jpg")
.addFamily(new StudentFamily("孔融", "", LocalDateTime.now(), "儿子"))
.addFamily(new StudentFamily("老子", "", LocalDateTime.now(), "陌生人"))
.addFamily(new StudentFamily("孔明灯", "", LocalDateTime.now(), "天灯"))
.build());
mStudentEntities.add(new StudentEntity.Builder()
.setName("李白")
.setUrl("")
.addFamily(new StudentFamily("李太白", "http://img0.imgtn.bdimg.com/it/u=3293099503,606929711&fm=26&gp=0.jpg", LocalDateTime.now(), "爸爸"))
.addFamily(new StudentFamily("李太黑", "", LocalDateTime.now(), "妈妈"))
.build());
mStudentEntities.add(new StudentEntity.Builder()
.setName("杜甫")
.setUrl("http://img0.imgtn.bdimg.com/it/u=3593446461,3335288407&fm=26&gp=0.jpg")
.addFamily(new StudentFamily("杜太甫", "http://img0.imgtn.bdimg.com/it/u=3293099503,606929711&fm=26&gp=0.jpg", LocalDateTime.now(), "爷爷"))
.addFamily(new StudentFamily("杜绢花", "https://img.pconline.com.cn/images/upload/upc/tx/photoblog/1405/25/c1/34592098_34592098_1400979781687_mthumb.jpg", LocalDateTime.now(), "妈妈"))
.build());
mStudentEntities.add(new StudentEntity.Builder()
.setName("白居易")
.setUrl("http://img2.imgtn.bdimg.com/it/u=1473741299,1011020019&fm=26&gp=0.jpg")
.addFamily(new StudentFamily("白行简", "http://img0.imgtn.bdimg.com/it/u=3293099503,606929711&fm=26&gp=0.jpg", LocalDateTime.now(), "姑姑"))
.addFamily(new StudentFamily("白天鹅", "", LocalDateTime.now(), "宠物"))
.addFamily(new StudentFamily("杜甫", "", LocalDateTime.now(), "兄弟"))
.build());
for (int i = 0; i < mStudentEntities.size(); i++) {
StudentEntity item = mStudentEntities.get(i);
char sort = getPinYinFirstLetter(item.getName().charAt(0));
if (mStudentMap.get(sort) == null) {
List
data.add(item);
mStudentMap.put(sort, data);
} else {
List
studentEntities.add(item);
}
}
}
private void initView() {
mRecyclerView = findViewById(R.id.recycleview);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false));
}
public char getPinYinFirstLetter(char str) {
String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(str);
if (pinyinArray==null){
return '#';
}
return pinyinArray==null?'#':Character.toUpperCase(pinyinArray[0].charAt(0));
}
}
StudentAdapter.java
RecyclerView的适配器,这里自定义了几个ViewGroup,如StudentViewGroup用来存放学生信息的视图列表。
import android.content.Context;
import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.hxl.gongzhonghaodemo.R;
import com.hxl.gongzhonghaodemo.dingding.entitys.StudentEntity;
import com.hxl.gongzhonghaodemo.dingding.ui.StudentViewGroup;
import java.util.List;
public class StudentAdapter extends RecyclerView.Adapter
private Context mContext;
private ArrayMap
public StudentAdapter(Context context, ArrayMap
this.mContext = context;
this.map = map;
}
@NonNull
@Override
public RecycleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View inflate = LayoutInflater.from(mContext).inflate(R.layout.item_group, parent, false);
return new RecycleViewHolder(inflate);
}
@Override
public void onBindViewHolder(@NonNull RecycleViewHolder holder, int position) {
holder.mTvSort.setText(map.keyAt(position)+"");
List
for (int i = 0; i holder.mStudentList.addStudent(studentEntities.get(i)); } } @Override public int getItemCount() { return map==null?0:map.size(); } public class RecycleViewHolder extends RecyclerView.ViewHolder{ TextView mTvSort; StudentViewGroup mStudentList; public RecycleViewHolder(@NonNull View itemView) { super(itemView); mTvSort=itemView.findViewById(R.id.tv_sort); mStudentList =itemView.findViewById(R.id.student_list); } } } 以下是学生信息的视图和家长信息的视图。 import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.Nullable; import com.hxl.gongzhonghaodemo.R; import com.hxl.gongzhonghaodemo.dingding.entitys.BaseInfo; import com.hxl.gongzhonghaodemo.dingding.entitys.StudentEntity; import com.hxl.gongzhonghaodemo.dingding.entitys.StudentFamily; import com.hxl.gongzhonghaodemo.dingding.utils.DisplayUtils; public class BaseItemLayout extends LinearLayout { public BaseItemLayout(Context context) { super(context); init(); } public BaseItemLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public BaseItemLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { setOrientation(LinearLayout.VERTICAL); } private View addItem(BaseInfo baseInfo, int resId) { if (baseInfo == null) { return null; } View mView = LayoutInflater.from(getContext()).inflate(resId, this, false); if (baseInfo instanceof StudentFamily){ ((TextView) mView.findViewById(R.id.tv_name)) .setText(((StudentFamily) baseInfo).getStudentEntity().getName()+"的"+((StudentFamily) baseInfo).getRelation()+"("+baseInfo.getName()+")"); }else { ((TextView) mView.findViewById(R.id.tv_name)).setText(baseInfo.getName()); } ProfilePicture mProfilePicture = mView.findViewById(R.id.im_profile_picture); int size = DisplayUtils.dip2px(getContext(), 40); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(size, size); mProfilePicture.setLayoutParams(params); mProfilePicture.setName(baseInfo); this.addView(mView); return mView; } public View addItem(BaseInfo baseInfo) { if (this instanceof StudentViewGroup) { return addItem(baseInfo, R.layout.item_student); } else { return addItem(baseInfo, R.layout.item_student_family); } } } public class StudentViewGroup extends BaseItemLayout { private static String TAG="StudentViewGroup"; private static Paint mNamePaint; public StudentViewGroup(Context context) { super(context); } public StudentViewGroup(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public StudentViewGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void addStudent(StudentEntity studentEntity){ //添加自个 View mView = addItem(studentEntity); //添加家人 FamilyViewGroup mFamilyList = mView.findViewById(R.id.family_list); mFamilyList.addFamilyName(studentEntity.getStudentFamilies()); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); } } import android.content.Context; import android.util.AttributeSet; import android.widget.LinearLayout; import androidx.annotation.Nullable; import com.hxl.gongzhonghaodemo.dingding.entitys.StudentFamily; import java.util.List; public class FamilyViewGroup extends BaseItemLayout { public FamilyViewGroup(Context context) { super(context); } public FamilyViewGroup(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public FamilyViewGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void addFamilyName(List for (int i = 0; i < studentFamilies.size(); i++) { addItem(studentFamilies.get(i)); } } } 其中自定义了个圆角视图RoundRelativeLayout和头像视图ProfilePicture。当没有头像url地址时,使用自己名字后两位,如果有,则使用Glide加载。 package com.hxl.gongzhonghaodemo.dingding.ui; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; import android.widget.RelativeLayout; public class RoundRelativeLayout extends RelativeLayout { private final RectF mRectF = new RectF(); private final Paint maskPaint = new Paint(); private final Paint zonePaint = new Paint(); private View mView; private Context mContext; public RoundRelativeLayout(Context context) { super(context); init(); } public RoundRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); } public RoundRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { maskPaint.setAntiAlias(true); maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); zonePaint.setColor(Color.WHITE); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); mRectF.set(0,0,getWidth(),getHeight()); } @Override public void draw(Canvas canvas) { canvas.saveLayer(mRectF, zonePaint, Canvas.ALL_SAVE_FLAG); canvas.drawRoundRect(mRectF, getWidth(), getWidth(), zonePaint); canvas.saveLayer(mRectF, maskPaint, Canvas.ALL_SAVE_FLAG); super.draw(canvas); canvas.restore(); } } import android.content.Context; import android.graphics.Color; import android.util.AttributeSet; import android.view.Gravity; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.bumptech.glide.Glide; import com.hxl.gongzhonghaodemo.dingding.entitys.BaseInfo; public class ProfilePicture extends RoundRelativeLayout { public ProfilePicture(Context context, AttributeSet attrs) { super(context, attrs); } public ProfilePicture(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public ProfilePicture(Context context) { super(context); } public void setName(BaseInfo baseInfo) { if (baseInfo.getProfilePicture() == null || baseInfo.getProfilePicture().length() == 0) { TextView mName = new TextView(getContext()); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); mName.setLayoutParams(params); String name = baseInfo.getName().length() > 2 ? baseInfo.getName().substring(baseInfo.getName().length() - 2) : baseInfo.getName(); mName.setText(name); mName.setGravity(Gravity.CENTER); this.addView(mName); mName.setTextColor(Color.WHITE); return; } ImageView imageView =new ImageView(getContext()); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); Glide.with(imageView).load(baseInfo.getProfilePicture()).into(imageView); this.addView(imageView); } }