效果图

Android仿微信消息列表_java


XML布局

item_wechat_message.xml

单个Item,自定义了一个圆角Layout,和一个消息角标

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="horizontal"    android:paddingLeft="15dp"    android:gravity="center"    android:layout_width="match_parent"    android:minHeight="72dp"    android:clipToPadding="false"    android:clipChildren="false"    android:background="@drawable/selector_main_wechat_chat"    android:layout_height="72dp">    <FrameLayout        android:clipToPadding="false"        android:clipChildren="false"        android:layout_width="48dp"        android:layout_height="48dp">        <com.example.myapplication.ui.widget.RoundLayout            android:background="#FFFFFF"            android:layout_width="match_parent"            android:layout_height="match_parent">            <ImageView                android:id="@+id/iv_author"                android:scaleType="centerCrop"                android:layout_width="match_parent"                android:layout_height="match_parent"></ImageView>        </com.example.myapplication.ui.widget.RoundLayout>        <com.example.myapplication.ui.widget.MessageMarker            android:id="@+id/tv_message_marker"            android:text="3"            android:gravity="center"            android:layout_marginTop="-4dp"            android:layout_marginRight="-4dp"            android:textSize="10sp"            android:textColor="#ffffff"            android:background="@drawable/shape_message_marker"            android:layout_gravity="right"            android:minWidth="10dp"            android:minHeight="10dp"            android:layout_width="wrap_content"            android:layout_height="wrap_content">
       </com.example.myapplication.ui.widget.MessageMarker>    </FrameLayout>
   <RelativeLayout        android:layout_marginLeft="8dp"        android:orientation="vertical"        android:layout_width="match_parent"        android:layout_height="match_parent">        <LinearLayout            android:layout_centerVertical="true"
           android:paddingRight="15dp"            android:orientation="vertical"            android:layout_width="match_parent"            android:layout_height="wrap_content">            <LinearLayout                android:orientation="horizontal"                android:layout_width="match_parent"                android:layout_height="25dp">                <TextView                    android:id="@+id/tv_name"                    android:textSize="17sp"                    android:textColor="#000000"                    android:gravity="center|left"                    android:text="电子学院乒乓球"                    android:layout_weight="1"                    android:layout_width="0dp"                    android:layout_height="match_parent">
               </TextView>                <TextView                    android:id="@+id/tv_last_timer"                    android:textSize="11sp"                    android:textColor="#9e9e9e"                    android:gravity="right"                    android:text="早上12:22"                    android:layout_width="80dp"                    android:layout_height="wrap_content">
               </TextView>            </LinearLayout>
           <LinearLayout                android:orientation="horizontal"                android:layout_width="match_parent"                android:layout_height="25dp">                <TextView                    android:id="@+id/tv_perview_message"                    android:textSize="12sp"                    android:gravity="center|left"                    android:text="[图片]"                    android:layout_weight="1"                    android:layout_width="0dp"                    android:layout_height="match_parent">
               </TextView>                <TextView                    android:gravity="right"                    android:text=""                    android:layout_width="50dp"                    android:layout_height="wrap_content">
               </TextView>            </LinearLayout>
       </LinearLayout>
       <View            android:layout_alignParentBottom="true"            android:background="@color/colorBaseLine"            android:layout_width="match_parent"            android:layout_height="2px"></View>    </RelativeLayout></LinearLayout>


wechat_activity_title.xml

顶部标题栏

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="match_parent"    android:paddingLeft="15dp"    android:gravity="center"    android:background="@color/colorMainTheme"    android:layout_height="48dp">    <TextView        android:textSize="18sp"        android:textColor="#000000"        android:layout_centerVertical="true"        android:text="微信(5)"        android:layout_width="wrap_content"        android:layout_height="wrap_content">
   </TextView>    <RelativeLayout        android:gravity="center"        android:layout_toLeftOf="@+id/rl_add"        android:layout_width="48dp"        android:layout_height="48dp">        <ImageView            android:id="@+id/search"            android:background="@drawable/ic_search"            android:layout_width="wrap_content"            android:layout_height="wrap_content"></ImageView>    </RelativeLayout>

   <RelativeLayout        android:id="@+id/rl_add"        android:gravity="center"        android:layout_alignParentRight="true"        android:layout_width="48dp"        android:layout_height="48dp">        <ImageView            android:id="@+id/add"
           android:layout_alignParentRight="true"            android:background="@drawable/ic_add"            android:layout_width="wrap_content"            android:layout_height="wrap_content"></ImageView>    </RelativeLayout>

</RelativeLayout>


wechat_buttom_nav.xml

底部导航栏

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="match_parent"    android:background="@color/colorMainTheme"    android:layout_height="56dp">    <View        android:background="@color/colorBaseLine"        android:layout_width="match_parent"        android:layout_height="2px"></View>    <LinearLayout        android:paddingTop="5dp"        android:paddingBottom="5dp"        android:orientation="horizontal"        android:layout_width="match_parent"        android:layout_height="match_parent">        <LinearLayout            android:orientation="vertical"            android:gravity="center"            android:layout_width="0dp"            android:layout_weight="1"            android:layout_height="match_parent">            <ImageView                android:background="@drawable/nav_item_index"                android:layout_width="wrap_content"                android:layout_height="wrap_content"></ImageView>            <TextView                android:layout_marginTop="2dp"                style="@style/MainNavItemTextView"                android:text="微信"                android:layout_width="wrap_content"                android:layout_height="wrap_content"></TextView>        </LinearLayout>
       <LinearLayout            android:orientation="vertical"            android:gravity="center"            android:layout_width="0dp"            android:layout_weight="1"            android:layout_height="match_parent">            <ImageView                android:background="@drawable/nav_firend"                android:layout_width="wrap_content"                android:layout_height="wrap_content"></ImageView>            <TextView
               style="@style/MainNavItemTextView"                android:text="通讯录"                android:layout_width="wrap_content"                android:layout_height="wrap_content"></TextView>        </LinearLayout>
       <LinearLayout            android:orientation="vertical"            android:gravity="center"            android:layout_width="0dp"            android:layout_weight="1"            android:layout_height="match_parent">            <ImageView                android:background="@drawable/nav_share"                android:layout_width="wrap_content"                android:layout_height="wrap_content"></ImageView>            <TextView                android:layout_marginTop="2dp"                style="@style/MainNavItemTextView"                android:text="发现"                android:layout_width="wrap_content"                android:layout_height="wrap_content"></TextView>        </LinearLayout>
       <LinearLayout            android:orientation="vertical"            android:gravity="center"            android:layout_width="0dp"            android:layout_weight="1"            android:layout_height="match_parent">            <ImageView                android:background="@drawable/nav_home"                android:layout_width="wrap_content"                android:layout_height="wrap_content"></ImageView>            <TextView                android:layout_marginTop="2dp"                style="@style/MainNavItemTextView"                android:text="我"                android:layout_width="wrap_content"                android:layout_height="wrap_content"></TextView>        </LinearLayout>
   </LinearLayout>
</LinearLayout>


activity_main.xml

主界面

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    android:background="#FFFFFF"    android:fitsSystemWindows="true"    tools:context=".MainActivity">    <include layout="@layout/wechat_activity_title"></include>
   <ListView        android:scrollbars="none"        android:dividerHeight="0dp"        android:divider="@null"        android:overScrollMode="never"        android:id="@+id/lv_main_chat"        android:layout_width="match_parent"        android:layout_weight="1"        android:layout_height="0dp">
   </ListView>
   <include layout="@layout/wechat_buttom_nav"></include></LinearLayout>


创建数据message_list.son,消息列表集合

[  {    "mEnumMessageType": "1",    "mUserName": "可可爱爱",    "mUserUrl": "https://img2.woyaogexing.com/2019/11/01/3729c227bf5e40d98275efbb50589ec5!400x400.jpeg",    "mPerviewMessage": "干嘛呢",    "mLastTimer": 1572600035475,    "mMessageMarkerCount": 1,    "mNotify": false  },  {    "mEnumMessageType": "1",    "mUserName": "刘",    "mUserUrl": "https://img2.woyaogexing.com/2019/11/01/40f21ea149bb459798e9e6d320901bbc!400x400.jpeg",    "mPerviewMessage": "[图片]",    "mLastTimer": 1572500025475,    "mMessageMarkerCount": 0,    "mNotify": false  },  {    "mEnumMessageType": "1",    "mUserName": "傻子",    "mUserUrl": "https://img2.woyaogexing.com/2019/11/01/e7972dc51d634b68b0b2bd03ef10d355!400x400.jpeg",    "mPerviewMessage": "行",    "mLastTimer": 1572400025475,    "mMessageMarkerCount": 2,    "mNotify": false  },  {    "mEnumMessageType": "1",    "mUserName": "杨旭",    "mUserUrl": "https://img2.woyaogexing.com/2019/11/01/a6ad8bbf6f9e4e5aa11181c61b6ef998!400x400.jpeg",    "mPerviewMessage": "拜拜",    "mLastTimer": 1572300025475,    "mMessageMarkerCount": 2,    "mNotify": false  }]

MainActivity.java

package com.example.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Build;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.ListView;
import com.example.myapplication.adapter.WechatChatBaseAdapter;import com.example.myapplication.bean.BaseItemWechatEntity;import com.example.myapplication.bean.FriendItemWechatEntity;import com.example.myapplication.enums.EnumMessageType;import com.example.myapplication.factory.ItemWeichatFactory;import com.example.myapplication.utils.LogUtils;
import org.json.JSONArray;import org.json.JSONException;import org.json.JSONObject;
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.Iterator;import java.util.List;
public class MainActivity extends AppCompatActivity {    private ListView mListView;    private List<BaseItemWechatEntity> mChatList;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {            getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);        }
       setContentView(R.layout.activity_main);        initView();        initData();
   }    private void initData(){        mChatList =new ArrayList<>();        try{           InputStream is = getResources().getAssets().open("message_list.json");            BufferedReader br =new BufferedReader(new InputStreamReader(is,"utf-8"));            StringBuffer stringBuffer =new StringBuffer();            while (true){                String temp =br.readLine();                if (temp==null){break;}                stringBuffer.append(temp);
           }            br.close();            handerListJson(stringBuffer.toString());        }catch (IOException e){            e.printStackTrace();        }
       mListView.setAdapter(new WechatChatBaseAdapter(mChatList,this));
   }    private void handerListJson(String json){        try{            JSONArray jsonArray =new JSONArray(json);            for (int i = 0; i < jsonArray.length(); i++) {                LogUtils.i(jsonArray.getString(i));             mChatList.add( createItemEntity(jsonArray.getJSONObject(i), EnumMessageType.FRIEND_MESSAGE));            }        }catch (JSONException e){            e.printStackTrace();        }    }    private BaseItemWechatEntity  createItemEntity(JSONObject json, EnumMessageType type){        return  ItemWeichatFactory.createItemWechatEntyty(type,                json.optInt("mLastTimer"),                json.optInt("mMessageMarkerCount"),                json.optBoolean("mNotify"),                json.optString("mPerviewMessage"),                json.optString("mUserName"),json.optString("mUserUrl"));
   }    private void  initView(){        mListView =findViewById(R.id.lv_main_chat);    }}



WechatChatBaseAdapter.java

适配器

package com.example.myapplication.adapter;
import android.content.Context;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;
import com.example.myapplication.R;import com.example.myapplication.bean.BaseItemWechatEntity;import com.example.myapplication.utils.DateUtils;import com.example.myapplication.utils.ImageLoader;import com.example.myapplication.utils.LogUtils;
import java.util.List;
public class WechatChatBaseAdapter extends BaseAdapter {    private List<BaseItemWechatEntity> mChatList;    private Context mContext;    private LayoutInflater mInflater;

   public WechatChatBaseAdapter(List<BaseItemWechatEntity> mChatList, Context mContext) {        this.mChatList = mChatList;        this.mContext = mContext;        mInflater=LayoutInflater.from(mContext);    }
   @Override    public int getCount() {        return mChatList==null?0:mChatList.size();    }
   @Override    public BaseItemWechatEntity getItem(int position) {        return mChatList.get(position);    }
   @Override    public long getItemId(int position) {        return position;    }
   @Override    public View getView(int position, View convertView, ViewGroup parent) {        if (convertView == null) {            convertView = mInflater.inflate(R.layout.item_wechat_message, null,false);            convertView.setTag(new ViewHolder(convertView));        }        initializeViews(getItem(position), (ViewHolder) convertView.getTag());        return convertView;    }
   private void initializeViews(BaseItemWechatEntity entity, ViewHolder holder) {        LogUtils.i(entity.toString());        holder.tvName.setText(entity.getUserName());        holder.tvPerviewMessage.setText(entity.getPerviewMessage());        holder.tvMessageMarker.setText(entity.getMessageMarkerCount()+"");        holder.tvLastTimer.setText(DateUtils.getDiffDate(entity.getLastTimer()));           ImageLoader.with(mContext).load(entity.getUserUrl(),holder.ivAuthor);        if (entity.getMessageMarkerCount()==0){holder.tvMessageMarker.setVisibility(View.GONE);}    }
   class  ViewHolder{        private ImageView ivAuthor;        private TextView tvName;        private TextView tvLastTimer;        private TextView tvPerviewMessage;        private TextView tvMessageMarker;        public ViewHolder(View view) {            ivAuthor = (ImageView) view.findViewById(R.id.iv_author);            tvName = (TextView) view.findViewById(R.id.tv_name);            tvLastTimer = (TextView) view.findViewById(R.id.tv_last_timer);            tvPerviewMessage = (TextView) view.findViewById(R.id.tv_perview_message);            tvMessageMarker=(TextView)view.findViewById(R.id.tv_message_marker);        }    }}


其中还有一个ImageLoad,用来加载图片

只是简单是使用BitmapFactory从流中构建一个Bitmap,没有做任何优化

package com.example.myapplication.utils;
import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Handler;import android.os.Looper;import android.widget.ImageView;
import com.example.myapplication.R;
import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.URL;
public class ImageLoader {    private Context mContext;    private Handler mHandler = new Handler(Looper.getMainLooper());
   public ImageLoader(Context context) {        this.mContext = context;    }
   public static ImageLoader with(Context context) {        return new ImageLoader(context);    }
   public void load(final String url, final ImageView imageView) {        new Thread() {            @Override            public void run() {                super.run();                Bitmap bitmap = getBitmapByUrl(url);                if (bitmap != null) {                    loadImage(bitmap, imageView);                }
           }        }.start();
   }
   private void loadImage(final Bitmap bitmap, final ImageView imageView) {
       mHandler.post(new Runnable() {            @Override            public void run() {                imageView.setImageBitmap(bitmap);            }        });    }
   private  Bitmap getBitmapByUrl(String urlString) {        try {            LogUtils.i(Thread.currentThread().getName());            URL url = new URL(urlString);            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
           InputStream inputStream = urlConnection.getInputStream();            Bitmap bitmap = BitmapFactory.decodeStream(inputStream);            LogUtils.i(bitmap + "");            return bitmap;        } catch (IOException e) {            e.printStackTrace();        }        return getDefault();    }    private Bitmap getDefault() {        return BitmapFactory.decodeResource(mContext.getResources(), R.drawable.back);    }}



BaseItemWechatEntity

实体类

package com.example.myapplication.bean;
import com.example.myapplication.enums.EnumMessageType;
public  class BaseItemWechatEntity {    private   EnumMessageType mEnumMessageType;
   private String mUserUrl;    private String mUserName;    private String mPerviewMessage;    private long mLastTimer;    private int mMessageMarkerCount;    private boolean isNotify;
   public BaseItemWechatEntity(Builder builder) {        this(builder,null);    }
   public BaseItemWechatEntity(Builder builder,EnumMessageType type) {        this.mEnumMessageType =type;
       this.isNotify=builder.isNotify;        this.mLastTimer=builder.mLastTimer;        this.mMessageMarkerCount=builder.mMessageMarkerCount;        this.mPerviewMessage=builder.mPerviewMessage;        this.mUserName=builder.mUserName;        this.mUserUrl=builder.mUserUrl;    }


   public EnumMessageType getEnumMessageType() {        return mEnumMessageType;    }
   public String getUserUrl() {        return mUserUrl;    }
   public String getUserName() {        return mUserName;    }
   public String getPerviewMessage() {        return mPerviewMessage;    }
   public long getLastTimer() {        return mLastTimer;    }
   public int getMessageMarkerCount() {        return mMessageMarkerCount;    }
   public boolean isNotify() {        return isNotify;    }


   @Override    public String toString() {        return "BaseItemWechatEntity{" +                "mEnumMessageType=" + mEnumMessageType +                ", mUserUrl='" + mUserUrl + '\'' +                ", mUserName='" + mUserName + '\'' +                ", mPerviewMessage='" + mPerviewMessage + '\'' +                ", mLastTimer=" + mLastTimer +                ", mMessageMarkerCount=" + mMessageMarkerCount +                ", isNotify=" + isNotify +                '}';    }    public  static  class  Builder{        private String mUserUrl;        private String mUserName;        private String mPerviewMessage;        private long mLastTimer;        private int mMessageMarkerCount;        private boolean isNotify;
       public Builder setUserUrl(String userUrl) {            mUserUrl = userUrl;            return this;        }
       public Builder setUserName(String userName) {            mUserName = userName;            return this;        }
       public Builder setPerviewMessage(String perviewMessage) {            mPerviewMessage = perviewMessage;            return this;        }
       public Builder setLastTimer(long lastTimer) {            mLastTimer = lastTimer;            return this;        }
       public Builder setMessageMarkerCount(int messageMarkerCount) {            mMessageMarkerCount = messageMarkerCount;            return this;        }
       public Builder setNotify(boolean notify) {            isNotify = notify;            return this;        }        public  BaseItemWechatEntity builder(){            return  new BaseItemWechatEntity(this);        }    } }


RoundLayout.java

圆角布局

package com.example.myapplication.ui.widget;
import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Path;import android.graphics.RectF;import android.util.AttributeSet;import android.view.MotionEvent;import android.widget.RelativeLayout;
public class RoundLayout extends RelativeLayout {    private Paint mPaint;    private Path mPath;    private float mRadius=15;//    private Bitmap mBitmap;//    private int mStartX,mStartY;//    private int mDownX,mDownY;    private RectF rectF;    public RoundLayout(Context context) {        super(context);        init();    }
   public RoundLayout(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }
   public RoundLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();
   }    private void init(){        mPaint= new Paint();        mPath =new Path();        rectF =new RectF();
   }
   @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        return false;    }
   @Override    public void draw(Canvas canvas) {        canvas.clipPath(genPath());        super.draw(canvas);    }
   @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        rectF.set(0,0,w,h);    }
   @Override    protected void dispatchDraw(Canvas canvas) {        super.dispatchDraw(canvas);    }        private Path genPath() {        mPath.reset();        mPath.addRoundRect(rectF, mRadius, mRadius, Path.Direction.CW);        return mPath;    }}