此篇综合运用自定义ActionBar、ContextMenu、PopupWindow、Fragment、ViewPager 以及RecyclerView等实现微信页面效果。 这个说小不小的作品花了我两天的时间,时间花费的颇多。如果我的作品对您有所帮助的话,您的关注或是赞,都是对我的莫大支持。如果引用我的作品,请注明出处。 我尽可能符合了作业的题目要求,但是有些内容由于作业要求的组件或是方法达不到微信的界面效果,我进行相应的替换,在此说明。 内容较多,我准备分成三篇博客进行叙述分别为: 安卓作业—-慕课移动应用开发作业15之模仿实现微信界面效果(二) 此篇是第三篇,主要写了fragment具体的实现部分,包括RecyclerView子布局的实现和一些效果。 废话说了那么多,先上效果图,如果各位看官还满意,那就继续读下去吧。 在res目录下新建menu目录,创建此文件 colors.xml styles.xml 如果有什么问题,请私信联系我或者在评论区留言
同时这也是中国大学慕课移动终端应用开发的网课作业15,我会持续更新我的作业说明
说明1
说明2
安卓作业—-慕课移动应用开发作业15之模仿实现微信界面效果(一)
安卓作业—-慕课移动应用开发作业15之模仿实现微信界面效果(三)说明3
效果图
代码
1.微信RecyclerView的实现
布局子文件 chat_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="https://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="65dp"> <RelativeLayout android:layout_marginLeft="5dp" android:layout_width="65dp" android:layout_height="match_parent"> <ImageView android:id="@+id/image_chat" android:src="@drawable/girl" android:layout_width="50dp" android:layout_height="50dp" android:layout_centerInParent="true"/> </RelativeLayout> <RelativeLayout android:layout_marginLeft="5dp" android:layout_width="0dp" android:layout_height="60dp" android:layout_weight="1"> <TextView android:id="@+id/text_chat_title" android:layout_width="wrap_content" android:layout_marginTop="12dp" android:text="相亲相爱一家人" android:textSize="18dp" android:textColor="#333" android:layout_height="25dp"/> <TextView android:id="@+id/text_chat_content" android:layout_below="@id/text_chat_title" android:layout_width="wrap_content" android:layout_height="20dp" android:text="老妈:[好心情]" android:textSize="10dp" android:textColor="#ccc"/> <TextView android:id="@+id/text_chat_date" android:layout_alignParentRight="true" android:layout_marginTop="15dp" android:layout_marginRight="20dp" android:text="周日" android:textColor="#ccc" android:layout_width="30dp" android:layout_height="20dp" android:textSize="12dp"/> <View android:layout_alignParentBottom="true" android:layout_width="match_parent" android:layout_height="1dp" android:background="#eee"/> </RelativeLayout> </LinearLayout>
对应的模型 ChatMsg.java
public class ChatMsg { private int imgId;//头像 private String title;//标题 private String content;//内容 private String date;//日期 /** * 完全体构造方法 * */ public ChatMsg(int imgId, String title, String content, String date) { this.imgId = imgId; this.title = title; this.content = content; this.date = date; } /** * 偷懒版 * */ public ChatMsg(String title, String content) { this.title = title; this.content = content; this.imgId = R.drawable.girl; this.date = "昨天"; } /** * 无参构造方法 * */ public ChatMsg() { } public int getImgId() { return imgId; } public void setImgId(int imgId) { this.imgId = imgId; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getDate() { return date; } public void setDate(String date) { this.date = date; } }
对应的适配器 ChatAdapter.java
/** * 微信聊天界面的适配器 * */ public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ViewHolder>{ private ArrayList<ChatMsg> mChatMsgs; private Context mContext;//上下文对象 private LayoutInflater mInflater; private int mPosition = -1;//记录当前位置 public int getPosition() { return mPosition; } public void setPosition(int position) { mPosition = position; } public void deleteItem(int position){ mChatMsgs.remove(position); notifyDataSetChanged(); } public void toFirstItem(int position){ ChatMsg chatMsg = mChatMsgs.remove(position); mChatMsgs.add(0,chatMsg); notifyDataSetChanged(); } public ChatAdapter(Context context, ArrayList<ChatMsg> chatMsgs) { mContext = context; mChatMsgs = chatMsgs; mInflater = LayoutInflater.from(mContext); } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = mInflater.inflate(R.layout.chat_item, parent, false); ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(@NonNull final ViewHolder holder, int position) { ChatMsg chatMsg = mChatMsgs.get(position); holder.mImageView.setImageResource(chatMsg.getImgId()); holder.mTextViewTitle.setText(chatMsg.getTitle()); holder.mTextViewContent.setText(chatMsg.getContent()); holder.mTextViewDate.setText(chatMsg.getDate()); holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { mPosition = holder.getAdapterPosition(); return false; } }); } @Override public int getItemCount() { return mChatMsgs.size(); } /** * view holder,实现上下文菜单接口 * */ class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener{ public ImageView mImageView; public TextView mTextViewTitle,mTextViewContent,mTextViewDate; public ViewHolder(@NonNull View itemView) { super(itemView); mImageView = itemView.findViewById(R.id.image_chat); mTextViewTitle = itemView.findViewById(R.id.text_chat_title); mTextViewContent = itemView.findViewById(R.id.text_chat_content); mTextViewDate = itemView.findViewById(R.id.text_chat_date); itemView.setOnCreateContextMenuListener(this); } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { new MenuInflater(mContext).inflate(R.menu.chat_item_menu, menu); } } }
2.通讯录RecyclerView的实现
布局子文件 contact_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="https://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="50dp"> <RelativeLayout android:layout_width="50dp" android:layout_height="match_parent" android:layout_marginLeft="5dp"> <ImageView android:id="@+id/image_contact" android:src="@drawable/girl" android:layout_centerInParent="true" android:layout_width="40dp" android:layout_height="40dp"/> </RelativeLayout> <RelativeLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"> <TextView android:id="@+id/text_contact_name" android:layout_centerVertical="true" android:layout_marginLeft="10dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="新的朋友" android:textSize="15dp" android:textColor="#333"/> <View android:layout_alignParentBottom="true" android:layout_width="match_parent" android:layout_height="1dp" android:background="#eee"/> </RelativeLayout> </LinearLayout>
对应的模型 ContactMsg.java
public class ContactMsg { private String groupName; //由于需要分组来给他们设置修饰需要一个组名 private int img; //图片资源 private String name; //名字 /** * 构造方法 * */ public ContactMsg(String groupName, int img, String name) { this.groupName = groupName; this.img = img; this.name = name; } /** * 偷懒的构造方法 * */ public ContactMsg(String groupName, String name) { this.groupName = groupName; this.name = name; this.img = R.drawable.girl; } public ContactMsg() { } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } public int getImg() { return img; } public void setImg(int img) { this.img = img; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
对应的适配器 ContactAdapter.java
/** * 通讯录联系人的适配器 * */ public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ViewHolder> { private Context mContext; private ArrayList<ContactMsg> mContactMsgs; private LayoutInflater mInflater; public ContactAdapter(Context context, ArrayList<ContactMsg> contactMsgs) { mContext = context; mContactMsgs = contactMsgs; mInflater = LayoutInflater.from(mContext); } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = mInflater.inflate(R.layout.contact_item, parent, false); ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { ContactMsg contactMsg = mContactMsgs.get(position); holder.mImageView.setImageResource(contactMsg.getImg()); holder.mTextView.setText(contactMsg.getName()); } @Override public int getItemCount() { return mContactMsgs.size(); } class ViewHolder extends RecyclerView.ViewHolder{ private ImageView mImageView; private TextView mTextView; public ViewHolder(@NonNull View itemView) { super(itemView); mImageView = itemView.findViewById(R.id.image_contact); mTextView = itemView.findViewById(R.id.text_contact_name); } } }
对应的装饰ContactItemDecoration .java
public class ContactItemDecoration extends RecyclerView.ItemDecoration{ private ArrayList<ContactMsg> mContactMsgs;//设置数据 private Paint mPaint;//设置画悬浮栏的画笔 private Rect mRectBounds;//设置一个矩形,用于画文字 private int mTitleHeight;//设置悬浮栏的高度 private int mTextSize;//设置文字大小 private Context mContext;//设置上下文对象 public ContactItemDecoration(Context context, ArrayList<ContactMsg> contactMsgs) { mContactMsgs = contactMsgs; mContext = context; //设置悬浮栏高度以及文字大小,为了统一尺寸规格,转换为像素 mTitleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, mContext.getResources().getDisplayMetrics()); mTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10, mContext.getResources().getDisplayMetrics()); mRectBounds = new Rect();//初始化矩形 //初始化画笔 mPaint = new Paint(); mPaint.setAntiAlias(true);//抗锯齿 mPaint.setDither(true);//防抖动 mPaint.setTextSize(mTextSize); } @Override public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { /** * 这个方法负责绘制每一个标题,可以实现随着视图移动而移动 * */ super.onDraw(c, parent, state); //先画出带有背景颜色的矩形条悬浮栏,从哪个位置开始绘制到哪个位置结束,则需要先确定位置,再画文字(即:title) int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); //父view(RecyclerView)有padding值,子view有margin值 int childCount = parent.getChildCount();//得到的数据其实是一屏可见的item数量并非总item数,再复用 for(int i = 0; i < childCount; i++){ View child = parent.getChildAt(i); //子view(即:item)有可能设置有margin值,所以需要parms来设置margin值 RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); //以及 获取 position 位置 int position = params.getViewLayoutPosition(); if(position > -1){ if (mContactMsgs.get(position).getGroupName().equals("0")){ //啥也不干 }else { if(position == 0){//肯定是要绘制一个悬浮栏 以及 悬浮栏内的文字 //画矩形悬浮条以及文字 drawRectAndText(c, left, right, child, params, position); }else{ if(mContactMsgs.get(position).getGroupName() != null && !mContactMsgs.get(position).getGroupName().equals(mContactMsgs.get(position - 1).getGroupName())){ //和上一个Tag不一样,说明是另一个新的分组 //画矩形悬浮条以及文字 drawRectAndText(c, left, right, child, params, position); }else{ //说明是一组的,什么也不画,共用同一个Tag } } } } } } @Override public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { /** * 这个方法设置预留空间 * */ super.getItemOffsets(outRect, view, parent, state); //获取position,由本方法的第三段注释可得 int position = parent.getChildAdapterPosition(view); if(position > -1){//界面中的所有子view if (mContactMsgs.get(position).getGroupName().equals("0")){ //啥也不干 }else { if(position == 0){//第一个位置,设置悬浮栏 //在top留出一段距离 outRect.set(0, mTitleHeight, 0, 0);//里面参数表示:左上右下的内边距padding距离 }else{ //当滑动到某一个item时(position位置)得到首字母,与上一个item对应的首字母不一致( position-1 位置),说明这是下一分组了 if(mContactMsgs.get(position).getGroupName() != null && !mContactMsgs.get(position).getGroupName().equals(mContactMsgs.get(position-1).getGroupName())){ //在top留出一段距离 outRect.set(0, mTitleHeight, 0, 0); }else{ //首字母相同说明是同一组的数据,比如都是 A 组下面的数据,那么就不需要再留出空间绘制悬浮栏了,共用同一个 A 组即可 outRect.set(0, 0, 0, 0); } } } } } /** * 绘制文字和图形 * */ private void drawRectAndText(Canvas c, int left, int right, View child, RecyclerView.LayoutParams params, int position) { //1、画矩形悬浮栏 //item可以有margin值不设置就默认为0,其中child.getTop()表示item距离父recycler view的距离,params.topMargin表示item的外边距,悬浮栏在item上方,那么悬浮栏的bottom就是child.getTop() - params.topMargin mPaint.setColor(Color.parseColor("#eeeeee")); c.drawRect(left, child.getTop() - params.topMargin - mTitleHeight, right, child.getTop() - params.topMargin, mPaint); //2、画文字 mPaint.setColor(Color.parseColor("#888888")); mPaint.getTextBounds(mContactMsgs.get(position).getGroupName(), 0, mContactMsgs.get(position).getGroupName().length(), mRectBounds);//将文字放到矩形中,得到Rect的宽高 c.drawText(mContactMsgs.get(position).getGroupName(), child.getPaddingLeft()+40, child.getTop() - params.topMargin - (mTitleHeight / 2 - mRectBounds.height() / 2), mPaint); } }
3.发现RecyclerView的实现
布局子文件 find_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="https://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="50dp"> <RelativeLayout android:layout_width="50dp" android:layout_height="match_parent" android:layout_marginLeft="5dp"> <ImageView android:id="@+id/image_find" android:src="@drawable/find_img1" android:layout_centerInParent="true" android:layout_width="30dp" android:layout_height="30dp"/> </RelativeLayout> <RelativeLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"> <TextView android:id="@+id/text_find_name" android:layout_centerVertical="true" android:layout_marginLeft="10dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="朋友圈" android:textSize="14dp" android:textColor="#333"/> <View android:layout_alignParentBottom="true" android:layout_width="match_parent" android:layout_height="1dp" android:background="#eee"/> <ImageView android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="10dp" android:src="@drawable/right" android:layout_width="15dp" android:layout_height="15dp"/> </RelativeLayout> </LinearLayout>
对应的模型 FindMsg.java
public class FindMsg { private int groupId; private int img; private String name; public FindMsg(int groupId, int img, String name) { this.groupId = groupId; this.img = img; this.name = name; } public FindMsg() { } public int getGroupId() { return groupId; } public void setGroupId(int groupId) { this.groupId = groupId; } public int getImg() { return img; } public void setImg(int img) { this.img = img; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
对应的适配器 FindAdapter.java
public class FindAdapter extends RecyclerView.Adapter<FindAdapter.ViewHolder> { private Context mContext; private ArrayList<FindMsg> mFindMsgs; private LayoutInflater mInflater; public FindAdapter(Context context, ArrayList<FindMsg> findMsgs) { mContext = context; mFindMsgs = findMsgs; mInflater = LayoutInflater.from(mContext); } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = mInflater.inflate(R.layout.find_item, parent, false); ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { FindMsg msg = mFindMsgs.get(position); holder.mImageView.setImageResource(msg.getImg()); holder.mTextViewName.setText(msg.getName()); } @Override public int getItemCount() { return mFindMsgs.size(); } class ViewHolder extends RecyclerView.ViewHolder{ private ImageView mImageView; private TextView mTextViewName; public ViewHolder(@NonNull View itemView) { super(itemView); mImageView = itemView.findViewById(R.id.image_find); mTextViewName = itemView.findViewById(R.id.text_find_name); } } }
对应的装饰FindItemDecoration .java
public class FindItemDecoration extends RecyclerView.ItemDecoration{ private ArrayList<FindMsg> mFindMsgs;//设置数据 private Paint mPaint;//设置画悬浮栏的画笔 private int mTitleHeight;//设置悬浮栏的高度 private Context mContext;//设置上下文对象 public FindItemDecoration(Context context, ArrayList<FindMsg> findMsgs) { mFindMsgs = findMsgs; mContext = context; //设置悬浮栏高度,为了统一尺寸规格,转换为像素 mTitleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, mContext.getResources().getDisplayMetrics()); //初始化画笔 mPaint = new Paint(); mPaint.setAntiAlias(true);//抗锯齿 mPaint.setDither(true);//防抖动 } @Override public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { /** * 这个方法负责绘制每一个标题,可以实现随着视图移动而移动 * */ super.onDraw(c, parent, state); //先画出带有背景颜色的矩形条悬浮栏,从哪个位置开始绘制到哪个位置结束,则需要先确定位置,再画文字(即:title) int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); //父view(RecyclerView)有padding值,子view有margin值 int childCount = parent.getChildCount();//得到的数据其实是一屏可见的item数量并非总item数,再复用 for(int i = 0; i < childCount; i++){ View child = parent.getChildAt(i); //子view(即:item)有可能设置有margin值,所以需要parms来设置margin值 RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); //以及 获取 position 位置 int position = params.getViewLayoutPosition(); if(position > -1){ if (mFindMsgs.get(position).getGroupId()==0){ //啥也不干 }else { if(position == 0){//肯定是要绘制一个悬浮栏 //画矩形悬浮条以及文字 drawRectAndText(c, left, right, child, params, position); }else{ if(mFindMsgs.get(position).getGroupId() != 0 && !(mFindMsgs.get(position).getGroupId()==mFindMsgs.get(position - 1).getGroupId())){ //和上一个Tag不一样,说明是另一个新的分组 //画矩形悬浮条以及文字 drawRectAndText(c, left, right, child, params, position); }else{ //说明是一组的,什么也不画,共用同一个Tag } } } } } } @Override public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { /** * 这个方法设置预留空间 * */ super.getItemOffsets(outRect, view, parent, state); //获取position,由本方法的第三段注释可得 int position = parent.getChildAdapterPosition(view); if(position > -1){//界面中的所有子view if (mFindMsgs.get(position).getGroupId()==0){ //啥也不干 }else { if(position == 0){//第一个位置,设置悬浮栏 //在top留出一段距离 outRect.set(0, mTitleHeight, 0, 0);//里面参数表示:左上右下的内边距padding距离 }else{ //当滑动到某一个item时(position位置)得到首字母,与上一个item对应的首字母不一致( position-1 位置),说明这是下一分组了 if(mFindMsgs.get(position).getGroupId() != 0 && !(mFindMsgs.get(position).getGroupId()==mFindMsgs.get(position - 1).getGroupId())){ //在top留出一段距离 outRect.set(0, mTitleHeight, 0, 0); }else{ //首字母相同说明是同一组的数据,比如都是 A 组下面的数据,那么就不需要再留出空间绘制悬浮栏了,共用同一个 A 组即可 outRect.set(0, 0, 0, 0); } } } } } /** * 绘制文字和图形 * */ private void drawRectAndText(Canvas c, int left, int right, View child, RecyclerView.LayoutParams params, int position) { //1、画矩形悬浮栏 //item可以有margin值不设置就默认为0,其中child.getTop()表示item距离父recycler view的距离,params.topMargin表示item的外边距,悬浮栏在item上方,那么悬浮栏的bottom就是child.getTop() - params.topMargin mPaint.setColor(Color.parseColor("#eeeeee")); c.drawRect(left, child.getTop() - params.topMargin - mTitleHeight, right, child.getTop() - params.topMargin, mPaint); } }
4.对遗漏的文件进行补充
菜单布局文件 chat_item_menu.xml
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="https://schemas.android.com/apk/res/android"> <item android:id="@+id/item1_toFirst" android:title="置顶"/> <item android:id="@+id/item2_delete" android:title="删除"/> </menu>
values文件夹下修改colors.xml和styles.xml文件
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#ddd</color> <color name="colorPrimaryDark">#ddd</color> <color name="colorAccent">#D81B60</color> </resources>
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> <style name="menu_layout1"> <item name="android:layout_width">120dp</item> <item name="android:layout_height">26dp</item> <item name="android:layout_marginBottom">5dp</item> </style> <style name="menu_image"> <item name="android:layout_width">20dp</item> <item name="android:layout_height">20dp</item> <item name="android:layout_gravity">center</item> </style> <style name="menu_text"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_marginLeft">8dp</item> <item name="android:layout_gravity">center</item> <item name="android:textSize">15dp</item> <item name="android:textColor">#333</item> </style> </resources>
总结
码字不易,若有帮助,给个关注和赞呗
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算