本系列持續更新中....java
前面學習了那麼多 UI 開發的知識,下面來進行實踐,作一個美觀的聊天界面。android
實戰前先學習一個小知識,如何製做 Nine-Patch 圖片。markdown
Nine-Patch 是一種被特殊處理的 .png
圖片,可以指定那些區域能夠被拉伸,那些區域不能夠。dom
來看看 Nine-Patch
圖片的實際做用。ide
首先咱們用一張普通的圖片做爲背景oop
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@mipmap/message_" android:orientation="vertical"> </LinearLayout> 複製代碼
運行結果佈局
能夠看到效果很是糟糕,因爲圖片的寬度不能填滿整個屏幕的寬度,整張圖片就被均勻的拉伸的,效果不好,這種狀況,咱們就可使用 Nine-Patch
圖片來進行改善了。學習
如何建立 nine-patch
圖片呢?this
首先在 Android Studio 中選中你要變成 nine-patch
的圖片,而後右擊--->Create 9-Patch file 就能夠建立 Nine-Patch 圖片了。 spa
咱們能夠在圖片的四個邊框繪製一個個的小黑點。在上邊框和左邊框的部分表示當前圖片須要拉伸的時候就會拉伸黑色點標記的區域,在下邊框和右邊框的部分表示內容會被放置的區域。用鼠標在圖片的邊緣拖到就能夠進行繪製了。按住 Shift
鍵拖動能夠進行擦除。
再來看看使用 nine-patch 的效果
這樣當圖片須要拉伸的時候就只拉伸指定區域了。
聊天界面確定有收到的消息和發送的消息,上面咱們已經把發送消息的背景圖製做好了,再製做一張發送消息的背景圖。
圖片資源都準備好了,就能夠寫代碼了。
編寫主頁面佈局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rlv" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <EditText android:id="@+id/et_info" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:hint="發送信息" android:maxLines="2"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="發送" android:id="@+id/bt_send"/> </LinearLayout> </LinearLayout> 複製代碼
創建聊天的消息對象
public class Msg { public static final int TYPE_RECEIVE = 0; public static final int TYPE_SEND = 1; private String content; public Msg(String content, int type) { this.content = content; this.type = type; } private int type; public String getContent() { return content; } public void setContent(String content) { this.content = content; } public int getType() { return type; } public void setType(int type) { this.type = type; } } 複製代碼
type 用來指定消息的類型,是發送的消息還接受的消息
而後編寫 RecyclerView 的子項佈局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:orientation="vertical"> <LinearLayout android:id="@+id/ll_left" android:background="@mipmap/message_left" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:id="@+id/tv_left" android:textColor="#FFF" android:layout_margin="10dp"/> </LinearLayout> <LinearLayout android:layout_gravity="right" android:id="@+id/ll_right" android:background="@mipmap/message_right" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:id="@+id/tv_right" android:layout_margin="10dp"/> </LinearLayout> </LinearLayout> 複製代碼
這裏咱們把接受消息和發送消息的佈局都寫進來了,代碼中根據消息的類型來調用 visible
方法,顯示對應的消息。
創建適配器
public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.MsgViewHolder> { private List<Msg> list; public MsgAdapter(List<Msg> list){ this.list = list; } class MsgViewHolder extends RecyclerView.ViewHolder{ LinearLayout llLeft,llRight; TextView tvLeft,tvRight; public MsgViewHolder(@NonNull View itemView) { super(itemView); llLeft =itemView.findViewById(R.id.ll_left); llRight =itemView.findViewById(R.id.ll_right); tvLeft =itemView.findViewById(R.id.tv_left); tvRight =itemView.findViewById(R.id.tv_right); } } @NonNull @Override public MsgViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.msg_item,parent,false); MsgViewHolder viewHolder = new MsgViewHolder(view); return viewHolder; } @Override public void onBindViewHolder(@NonNull MsgViewHolder holder, int position) { Msg msg = list.get(position); // 這裏根據消息的類型來選擇不一樣的佈局 if (msg.getType() == Msg.TYPE_RECEIVE){ holder.llLeft.setVisibility(View.VISIBLE); holder.llRight.setVisibility(View.GONE); holder.tvLeft.setText(msg.getContent()); }else { holder.llRight.setVisibility(View.VISIBLE); holder.llLeft.setVisibility(View.GONE); holder.tvRight.setText(msg.getContent()); } } @Override public int getItemCount() { return list.size(); } } 複製代碼
而後寫 Activity 代碼
public class MsgActivity extends AppCompatActivity { List<Msg> list =new ArrayList<>(); EditText text ; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_nine_patch); initData(); final RecyclerView recyclerView = findViewById(R.id.rlv); final MsgAdapter msgAdapter = new MsgAdapter(list); text = findViewById(R.id.et_info); Button bt = findViewById(R.id.bt_send); LinearLayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); recyclerView.setAdapter(msgAdapter); bt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Random random = new Random(); // 這裏仍是利用隨機數來生成消息的類型 int count = random.nextInt(20); Msg msg = new Msg(text.getText()+"count:"+count,count%2); list.add(msg); // 表示在消息的末尾插入內容 msgAdapter.notifyItemInserted(list.size()-1); // 讓 RecyclerView 自動滾動到最底部 recyclerView.scrollToPosition(list.size()-1); // 清空內容 text.setText(""); } }); } public void initData(){ Random random = new Random(); for (int i=0;i<40;i++){ int count = random.nextInt(20); Msg msg = new Msg("消息嗯哼"+i+"count:"+count,count%2); list.add(msg); } } } 複製代碼
notifyItemInserted()
方法,用於通知列表有新的數據插入了,這樣新增長的一條消息才能顯示出來。
scrolltoPosition()
方法將數據定位到最後一行,保證咱們能夠看到最後發送的內容。