(the code is too long, you can download it at http://blog.houxinlin.com/project/android/dinging.tar.gz)
1, Renderings
2, Implementation process
First of all, define the data information class, including the basic name, head portrait, and joining time. Parents and students expand from BaseInfo
Student information is defined as follows, and a parent set is expanded.
Parent information is defined as follows, which extends the name of the relationship with the student and holds a copy of the student's information.
MainActivity.java
The student and parent information here is saved using the ArrayMap provided by Android. The key is a single letter, the value is a collection of studententies, and studententies contain a collection of studentfamilies to store multiple parent information.
First, extract all students' initials and store them in ArrayMap (ArrayMap can access elements through subscripts). Finally, a single letter corresponds to multiple students, and a single student corresponds to multiple parent information structure.
The framework of com. Bellerweb: Pinyin4 can be used to extract Pinyin.
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<Character, List<StudentEntity>> 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<StudentEntity> mStudentEntities = new ArrayList<>(); mStudentEntities.add(new StudentEntity.Builder() .setName("Lu Xun") .setUrl("http://img5.imgtn.bdimg.com/it/u=3841760432,773912449&fm=11&gp=0.jpg") .addFamily(new StudentFamily("Lu Yu", "", LocalDateTime.now(), "Stranger")) .addFamily(new StudentFamily("Lu Zhishen", "", LocalDateTime.now(), "Stranger")) .build()); mStudentEntities.add(new StudentEntity.Builder() .setName("Confucius") .setUrl("http://img2.imgtn.bdimg.com/it/u=3927250071,3411280749&fm=26&gp=0.jpg") .addFamily(new StudentFamily("Kong Rong", "", LocalDateTime.now(), "Son")) .addFamily(new StudentFamily("Lao Tzu", "", LocalDateTime.now(), "Stranger")) .addFamily(new StudentFamily("Kongming latern", "", LocalDateTime.now(), "Sky lantern")) .build()); mStudentEntities.add(new StudentEntity.Builder() .setName("Li Bai") .setUrl("") .addFamily(new StudentFamily("Li Tai Bai", "http://img0.imgtn.bdimg.com/it/u=3293099503,606929711&fm=26&gp=0.jpg", LocalDateTime.now(), "Dad")) .addFamily(new StudentFamily("Li Tai Hai", "", LocalDateTime.now(), "Mom")) .build()); mStudentEntities.add(new StudentEntity.Builder() .setName("Du Fu") .setUrl("http://img0.imgtn.bdimg.com/it/u=3593446461,3335288407&fm=26&gp=0.jpg") .addFamily(new StudentFamily("Du Tai Fu", "http://img0.imgtn.bdimg.com/it/u=3293099503,606929711&fm=26&gp=0.jpg", LocalDateTime.now(), "grandpa")) .addFamily(new StudentFamily("Silk flower", "https://img.pconline.com.cn/images/upload/upc/tx/photoblog/1405/25/c1/34592098_34592098_1400979781687_mthumb.jpg", LocalDateTime.now(), "Mom")) .build()); mStudentEntities.add(new StudentEntity.Builder() .setName("Bai Juyi") .setUrl("http://img2.imgtn.bdimg.com/it/u=1473741299,1011020019&fm=26&gp=0.jpg") .addFamily(new StudentFamily("White bamboo slip", "http://img0.imgtn.bdimg.com/it/u=3293099503,606929711&fm=26&gp=0.jpg", LocalDateTime.now(), "aunt")) .addFamily(new StudentFamily("White Swan", "", LocalDateTime.now(), "Pets")) .addFamily(new StudentFamily("Du Fu", "", LocalDateTime.now(), "Brother")) .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<StudentEntity> data = new ArrayList<>(); data.add(item); mStudentMap.put(sort, data); } else { List<StudentEntity> 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 adapter. Here you have customized several view groups, such as the view list used by StudentViewGroup to store student information.
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<StudentAdapter.RecycleViewHolder> { private Context mContext; private ArrayMap<Character,List<StudentEntity>> map; public StudentAdapter(Context context, ArrayMap<Character, List<StudentEntity>> 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<StudentEntity> studentEntities = map.get(map.keyAt(position)); for (int i = 0; i <studentEntities.size() ; 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); } } }
The following are views of student information and parent information.
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()+"Of"+((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){ //Add self View mView = addItem(studentEntity); //Add family members 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<StudentFamily> studentFamilies) { for (int i = 0; i < studentFamilies.size(); i++) { addItem(studentFamilies.get(i)); } } }
Among them, round relative layout and profile picture are customized. When there is no url address for the avatar, use the last two digits of your name. If there is one, use Glide to load it.
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); } }