(代码过长,可以到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> mStudentMap;
@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 = new ArrayList<>();
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 = new ArrayList<>();
data.add(item);
mStudentMap.put(sort, data);
} else {
List studentEntities = mStudentMap.get(sort);
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> map;
public StudentAdapter(Context context, ArrayMap> map) {
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 studentEntities = map.get(map.keyAt(position));
for (int i = 0; i
以下是学生信息的视图和家长信息的视图。
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 studentFamilies) {
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);
}
}