Android9.0 Pie Java
I recently created a chat app in Android Java and implemented LINE-style chat list deletion. There are quite a few articles about the list display and deletion function of RecyclerView,
Swipe horizontally → Show dialog → Delete MainActivity + Multiple Fragment Configuration
I couldn't find the implementation example of in the Japanese article, so I will share it. (I don't know if it's a best practice, so please use it as a reference.
You can implement like this
The content is very long, so if you can't get the finished product, please click here. https://github.com/yuta-matsumoto/chat
Omit build files and manifest files
Chat ├app/src/main/ ├java/ │ └com.example.chat/ │ ├fragments/ │ │ ├BaseFragment.java │ │ ├ChatListFragment.java │ │ └DeleteChatFragment.java │ ├helpers/ │ │ ├ChatListAdapter.java │ │ ├ViewHolder.java │ │ └SwipeHelper.java │ ├models/ │ │ ├ChatRowData.java │ │ └DeleteChatRow.java │ └MainAcitivity.java ├res/ │ ├drawable/ │ │ └ sample1.png (Omitted below) │ ├layout/ │ │ ├activity_main.xml │ │ ├chat_list_row.xml │ │ └fragment_chat_list.xml │ └values/ │ ├colors.xml │ ├strings.xml │ └styles.xml
build.gradle
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0' //Postscript
implementation 'androidx.cardview:cardview:1.0.0' //Postscript
}
Activity
MainActivity only loads Fragment.
Fragment inherits the base class of all Fragments called BaseFragment to realize MainActivity + multiple Fragment configurations
.
I refer to the following articles. https://www.slideshare.net/olrandir/android-the-single-activity-multiple-fragments-pattern-one-activity-to-rule-them-all
MainActivity.java
package com.example.chat;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.example.chat.fragments.BaseFragment;
import com.example.chat.fragments.ChatListFragment;
public class MainActivity extends AppCompatActivity {
//Fragment base
private BaseFragment fragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Set Fragment of chat list for the first time
if (fragment == null) {
fragment = new ChatListFragment();
}
// main_Set Fragment to activity
getSupportFragmentManager().beginTransaction()
.replace(R.id.mainContainer, fragment)
.commit();
}
}
Fragment
This is the base Fragment. I have not included it this time, but it is convenient to include the event listener of the common button in BaseFragment because it will be compact.
BaseFragment.java
package com.example.chat.fragments;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
/**
*Base Fragment
*/
public abstract class BaseFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
Fragment of the chat list screen.
ChatListFragment.java
package com.example.chat.fragments;
import android.graphics.Color;
import android.os.Bundle;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.chat.models.DeleteChatRowData;
import com.example.chat.helpers.ChatListAdapter;
import com.example.chat.models.ChatRowData;
import com.example.chat.R;
import com.example.chat.helpers.SwipeHelper;
import java.util.ArrayList;
import java.util.List;
/**
*Fragment for chat list screen
*/
public class ChatListFragment extends BaseFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_chat_list, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
RecyclerView rv = view.findViewById(R.id.recyclerView);
//Chat list data List
final List list = getChatList();
//Number of elements in the data List of the chat list
final int itemCount = list.size();
//Chat list adapter
final ChatListAdapter adapter = new ChatListAdapter(list) {
@Override
public void onItemClick(View view, int pos, List<ChatRowData> list) {
//Added the process when clicking a line
}
};
LinearLayoutManager llm = new LinearLayoutManager(getContext());
rv.setHasFixedSize(true);
rv.setLayoutManager(llm);
rv.setAdapter(adapter);
//Implement swipe
SwipeHelper swipeHelper = new SwipeHelper(getContext(), rv) {
@Override
public void instantiateUnderlayButton(RecyclerView.ViewHolder viewHolder, List<UnderlayButton> underlayButtons) {
underlayButtons.add(new SwipeHelper.UnderlayButton(
getResources().getString(R.string.chat_list_delete_button_label),
0,
Color.parseColor(getResources().getString(R.string.chat_list_delete_button_color)),
new SwipeHelper.UnderlayButtonClickListener() {
@Override
public void onClick(int pos) {
FragmentManager fragmentManager = getFragmentManager();
DeleteChatRowFragment fragment = new DeleteChatRowFragment();
//Set the row data to be deleted in the delete dialog fragment
DeleteChatRowData deleteChatRowData = new DeleteChatRowData();
deleteChatRowData.setList(list);
deleteChatRowData.setAdapter(adapter);
deleteChatRowData.setPosition(pos);
deleteChatRowData.setItemCount(itemCount);
//Pass data using bundle
Bundle bundle = new Bundle();
bundle.putSerializable(getResources().getString(R.string.delete_dialog_list_tag), deleteChatRowData);
fragment.setArguments(bundle);
//Dialog display
fragment.show(fragmentManager, "delete chat list");
}
}
));
}
};
}
/**
*Chat list test data generation
*/
private List<ChatRowData> getChatList() {
List<ChatRowData> list = new ArrayList<>();
ChatRowData data1 = new ChatRowData();
data1.setName("Taro Tanaka");
data1.setText("Hello");
data1.setMessageDateTime("2020/6/09 13:00");
data1.setProfileImageId(R.drawable.sample1);
list.add(data1);
ChatRowData data2 = new ChatRowData();
data2.setName("Shigeru Sato");
data2.setText("Good morning!");
data2.setMessageDateTime("2020/6/08 8:10");
data2.setProfileImageId(R.drawable.sample2);
list.add(data2);
ChatRowData data3 = new ChatRowData();
data3.setName("taro");
data3.setText("What time is it?");
data3.setMessageDateTime("2020/6/07 20:09");
data3.setProfileImageId(R.drawable.sample3);
list.add(data3);
ChatRowData data4 = new ChatRowData();
data4.setName("hanako");
data4.setText("Please lend me a textbook");
data4.setMessageDateTime("2020/6/06 07:00");
data4.setProfileImageId(R.drawable.sample4);
list.add(data4);
ChatRowData data5 = new ChatRowData();
data5.setName("Tanaka");
data5.setText("Impossible");
data5.setMessageDateTime("2020/6/06 01:05");
data5.setProfileImageId(R.drawable.sample5);
list.add(data5);
ChatRowData data6 = new ChatRowData();
data6.setName("Kobayashi");
data6.setText("OK");
data6.setMessageDateTime("2020/6/05 14:22");
data6.setProfileImageId(R.drawable.sample6);
list.add(data6);
ChatRowData data7 = new ChatRowData();
data7.setName("Petagine");
data7.setText("I want to go home");
data7.setMessageDateTime("2020/6/05 13:00");
data7.setProfileImageId(R.drawable.sample7);
list.add(data7);
ChatRowData data8 = new ChatRowData();
data8.setName("Hayato");
data8.setText("Let's go see the movie, senior!");
data8.setMessageDateTime("2020/6/04 21:50");
data8.setProfileImageId(R.drawable.sample8);
list.add(data8);
ChatRowData data9 = new ChatRowData();
data9.setName("Tom");
data9.setText("lol");
data9.setMessageDateTime("2020/5/30 2:30");
data9.setProfileImageId(R.drawable.sample9);
list.add(data9);
ChatRowData data10 = new ChatRowData();
data10.setName("y.matsumoto");
data10.setText("I did it");
data10.setMessageDateTime("2020/5/29 4:00");
data10.setProfileImageId(R.drawable.sample10);
list.add(data10);
return list;
}
}
Fragment of the delete dialog.
DeleteChatRowFragment.java
package com.example.chat.fragments;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.Gravity;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.recyclerview.widget.RecyclerView;
import com.example.chat.R;
import com.example.chat.models.DeleteChatRowData;
import java.util.List;
/**
*Fragment for delete dialog
*/
public class DeleteChatRowFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// setCancelable(false)Does not close even if you press outside the dialog with
this.setCancelable(false);
TextView title = new TextView(getContext());
title.setText(getResources().getString(R.string.delete_dialog_message));
title.setPadding(10, 50, 10, 10);
title.setGravity(Gravity.CENTER);
return new AlertDialog.Builder(getActivity())
.setCustomTitle(title)
//When OK is pressed
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//Delete process
Bundle bundle = getArguments();
DeleteChatRowData deleteChatRowData =
(DeleteChatRowData) bundle.getSerializable(getResources().getString(R.string.delete_dialog_list_tag));
List list = deleteChatRowData.getList();
RecyclerView.Adapter adapter = deleteChatRowData.getAdapter();
int pos = deleteChatRowData.getPosition();
int itemCount = deleteChatRowData.getItemCount();
//Removed the element of the position order of the pressed line from the chat list List
list.remove(pos);
//Notify the adapter that the element has been deleted
deleteChatRowData.getAdapter().notifyItemRemoved(pos);
//Notify that there is a change in the chat list and rebind
adapter.notifyItemRangeChanged(pos, itemCount);
}
})
//When Cancel is pressed
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//Swipe back
Bundle bundle = getArguments();
DeleteChatRowData deleteChatRowData =
(DeleteChatRowData) bundle.getSerializable(getResources().getString(R.string.delete_dialog_list_tag));
RecyclerView.Adapter adapter = deleteChatRowData.getAdapter();
int pos = deleteChatRowData.getPosition();
//Swipe back
adapter.notifyItemChanged(pos);
}
})
.create();
}
//Don't quit when the app goes to the background
@Override
public void onPause() {
super.onPause();
dismiss();
}
}
Used to list chats.
ChatListAdapter.java
package com.example.chat.helpers;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import com.example.chat.R;
import com.example.chat.models.ChatRowData;
import java.util.List;
/**
*Adapter class used to display chat list
*/
public class ChatListAdapter extends RecyclerView.Adapter<ViewHolder> {
private List<ChatRowData> list;
public ChatListAdapter(List<ChatRowData> list) {
this.list = list;
}
/**
*Create ViewHolder for chat list
*/
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//Load one line of layout into View
View inflate = LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_list_row, parent, false);
final ViewHolder vh = new ViewHolder(inflate);
//Register click listener
inflate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Get the position of the clicked row
int position = vh.getAdapterPosition();
//Since the operation of View must be handled by Activity or Fragment, the actual processing is not written.
onItemClick(v, position, list);
}
});
//Register touch listener
inflate.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
//Since the operation of View must be handled by Activity or Fragment, the actual processing is not written.
return onItemTouch(v);
}
});
return vh;
}
/**
*Bind the data of chat list List to View in ViewHolder
*/
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
String messageDateTime = list.get(position).getMessageDateTime();
holder.nameView.setText(list.get(position).getName());
holder.textView.setText(list.get(position).getText());
holder.timeView.setText(messageDateTime);
holder.profileView.setImageResource(list.get(position).getProfileImageId());
}
/**
*Set the number of elements in the chat list List
*/
@Override
public int getItemCount() {
return list.size();
}
/**
*Override and process with ChatListFragment
*/
public void onItemClick(View view, int pos, List<ChatRowData> list) {
;
}
/**
*Override and process with ChatListFragment
*/
public boolean onItemTouch(View view) {
return false;
}
}
ViewHolder.java
package com.example.chat.helpers;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.example.chat.R;
/**
*ViewHolder class
*Define the View that makes up one line
*/
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView nameView;
public TextView textView;
public TextView timeView;
public ImageView profileView;
public ViewHolder(View itemView) {
super(itemView);
nameView = itemView.findViewById(R.id.name);
textView = itemView.findViewById(R.id.text);
timeView = itemView.findViewById(R.id.time);
profileView = itemView.findViewById(R.id.profileImage);
}
}
Used for horizontal swipe movement. I referred to this article quite a bit.
https://www.it-swarm.dev/ja/android/%E3%82%B9%E3%83%AF%E3%82%A4%E3%83%97%E3%81%AErecyclerview-itemtouchhelper%E3%83%9C%E3%82%BF%E3%83%B3/833735822/amp/
SwipeHelper.java
package com.example.chat.helpers;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
/**
*Swipe helper class
*/
public abstract class SwipeHelper extends ItemTouchHelper.SimpleCallback {
//DELETE button width displayed by swiping
public static final int BUTTON_WIDTH = 230;
private RecyclerView recyclerView;
private List<UnderlayButton> buttons;
private GestureDetector gestureDetector;
private int swipedPos = -1;
private float swipeThreshold = 0.5f;
private Map<Integer, List<UnderlayButton>> buttonsBuffer;
private Queue<Integer> recoverQueue;
private GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
for (UnderlayButton button : buttons) {
if (button.onClick(e.getX(), e.getY()))
break;
}
return true;
}
};
private View.OnTouchListener onTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent e) {
if (swipedPos < 0) return false;
Point point = new Point((int) e.getRawX(), (int) e.getRawY());
RecyclerView.ViewHolder swipedViewHolder = recyclerView.findViewHolderForAdapterPosition(swipedPos);
View swipedItem = swipedViewHolder.itemView;
Rect rect = new Rect();
swipedItem.getGlobalVisibleRect(rect);
if (e.getAction() == MotionEvent.ACTION_DOWN
|| e.getAction() == MotionEvent.ACTION_UP
|| e.getAction() == MotionEvent.ACTION_MOVE) {
if (rect.top < point.y && rect.bottom > point.y)
gestureDetector.onTouchEvent(e);
else {
recoverQueue.add(swipedPos);
swipedPos = -1;
recoverSwipedItem();
}
}
return false;
}
};
public SwipeHelper(Context context, RecyclerView recyclerView) {
super(0, ItemTouchHelper.LEFT);
this.recyclerView = recyclerView;
this.buttons = new ArrayList<>();
this.gestureDetector = new GestureDetector(context, gestureListener);
this.recyclerView.setOnTouchListener(onTouchListener);
buttonsBuffer = new HashMap<>();
recoverQueue = new LinkedList<Integer>() {
@Override
public boolean add(Integer o) {
if (contains(o))
return false;
else
return super.add(o);
}
};
attachSwipe();
}
@Override
public boolean onMove(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int pos = viewHolder.getAdapterPosition();
if (swipedPos != pos)
recoverQueue.add(swipedPos);
swipedPos = pos;
if (buttonsBuffer.containsKey(swipedPos))
buttons = buttonsBuffer.get(swipedPos);
else
buttons.clear();
buttonsBuffer.clear();
swipeThreshold = 0.5f * buttons.size() * BUTTON_WIDTH;
recoverSwipedItem();
}
@Override
public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
return swipeThreshold;
}
@Override
public float getSwipeEscapeVelocity(float defaultValue) {
return 0.1f * defaultValue;
}
@Override
public float getSwipeVelocityThreshold(float defaultValue) {
return 5.0f * defaultValue;
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
float dX, float dY, int actionState, boolean isCurrentlyActive) {
int pos = viewHolder.getAdapterPosition();
float translationX = dX;
View itemView = viewHolder.itemView;
if (pos < 0) {
swipedPos = pos;
return;
}
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
if (dX < 0) {
List<UnderlayButton> buffer = new ArrayList<>();
if (!buttonsBuffer.containsKey(pos)) {
instantiateUnderlayButton(viewHolder, buffer);
buttonsBuffer.put(pos, buffer);
} else {
buffer = buttonsBuffer.get(pos);
}
translationX = dX * buffer.size() * BUTTON_WIDTH / itemView.getWidth();
drawButtons(c, itemView, buffer, pos, translationX);
}
}
super.onChildDraw(c, recyclerView, viewHolder, translationX, dY, actionState, isCurrentlyActive);
}
private synchronized void recoverSwipedItem() {
while (!recoverQueue.isEmpty()) {
int pos = recoverQueue.poll();
if (pos > -1) {
recyclerView.getAdapter().notifyItemChanged(pos);
}
}
}
private void drawButtons(Canvas c, View itemView, List<UnderlayButton> buffer, int pos, float dX) {
float right = itemView.getRight();
float dButtonWidth = (-1) * dX / buffer.size();
for (UnderlayButton button : buffer) {
float left = right - dButtonWidth;
button.onDraw(
c,
new RectF(
left,
itemView.getTop(),
right,
itemView.getBottom()
),
pos
);
right = left;
}
}
public void attachSwipe() {
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(this);
itemTouchHelper.attachToRecyclerView(recyclerView);
}
public abstract void instantiateUnderlayButton(RecyclerView.ViewHolder viewHolder, List<UnderlayButton> underlayButtons);
public static class UnderlayButton {
private String text;
private int imageResId;
private int color;
private int pos;
private RectF clickRegion;
private UnderlayButtonClickListener clickListener;
public UnderlayButton(String text, int imageResId, int color, UnderlayButtonClickListener clickListener) {
this.text = text;
this.imageResId = imageResId;
this.color = color;
this.clickListener = clickListener;
}
public boolean onClick(float x, float y) {
if (clickRegion != null && clickRegion.contains(x, y)) {
clickListener.onClick(pos);
return true;
}
return false;
}
public void onDraw(Canvas c, RectF rect, int pos) {
Paint p = new Paint();
//Background color set
p.setColor(color);
c.drawRect(rect, p);
//DELETE text color set
p.setColor(Color.WHITE);
p.setTextSize(50);
Rect r = new Rect();
float cHeight = rect.height();
float cWidth = rect.width();
p.setTextAlign(Paint.Align.LEFT);
p.getTextBounds(text, 0, text.length(), r);
float x = cWidth / 2f - r.width() / 2f - r.left;
float y = cHeight / 2f + r.height() / 2f - r.bottom;
c.drawText(text, rect.left + x, rect.top + y, p);
clickRegion = rect;
this.pos = pos;
}
}
public interface UnderlayButtonClickListener {
void onClick(int pos);
}
}
Fill the data for one line of the chat list.
ChatRowData.java
package com.example.chat.models;
/**
*One line of data model class
*/
public class ChatRowData {
private String name;
private String text;
private String messageDateTime;
private int profileImageId;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getMessageDateTime() {
return messageDateTime;
}
public void setMessageDateTime(String messageDateTime) {
this.messageDateTime = messageDateTime;
}
public int getProfileImageId() {
return profileImageId;
}
public void setProfileImageId(int profileImageId) {
this.profileImageId = profileImageId;
}
}
Pack the data needed for the delete dialog.
ChatRowData.java
package com.example.chat.models;
/**
*One line of data model class
*/
public class ChatRowData {
private String name;
private String text;
private String messageDateTime;
private int profileImageId;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getMessageDateTime() {
return messageDateTime;
}
public void setMessageDateTime(String messageDateTime) {
this.messageDateTime = messageDateTime;
}
public int getProfileImageId() {
return profileImageId;
}
public void setProfileImageId(int profileImageId) {
this.profileImageId = profileImageId;
}
}
Only FrameLayout for inserting Fragment is described in activity_main. Actually, if the header and footer are also made into common parts, maintainability will improve, but
-The point that the transition animation to the chat screen to be added (feeling that the chat screen layer covers the entire screen from the right) becomes difficult. ・ The point that I fell into hell when I combined the behavior of the back key
I forgot to make common parts. .. I would be really happy if I could hear about improvement measures for experts in the next article. ..
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusableInTouchMode="true"
tools:context=".MainActivity">
<FrameLayout
android:id="@+id/mainContainer"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
This is the fragment layout for the chat list screen.
fragment_chat_list.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background_light_gray"
tools:context=".fragments.ChatListFragment">
<View
android:id="@+id/headerView"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:background="@color/background_dark_gray"
android:contextClickable="false"
android:layerType="none"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/chatListSubject"
android:layout_width="63dp"
android:layout_height="19dp"
android:text="@string/chat_list_subject_label"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@color/font_color_black"
app:layout_constraintBottom_toBottomOf="@+id/headerView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/headerView"
app:layout_constraintVertical_bias="0.48" />
<View
android:id="@+id/headerBorder"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginTop="48dp"
android:background="@color/background_border"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/headerView" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="0dp"
android:isScrollContainer="false"
app:layout_constraintBottom_toTopOf="@+id/footerBorder"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/headerView"
app:layout_constraintVertical_bias="0.0">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</LinearLayout>
<View
android:id="@+id/background"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:background="@color/background_dark_gray"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/footerBorder" />
<ImageButton
android:id="@+id/homeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="80dp"
android:background="@color/background_transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/footerBorder"
app:srcCompat="@drawable/home" />
<ImageButton
android:id="@+id/chatListButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/background_transparent"
app:layout_constraintEnd_toStartOf="@+id/userButton"
app:layout_constraintStart_toEndOf="@+id/homeButton"
app:layout_constraintTop_toTopOf="@+id/homeButton"
app:srcCompat="@drawable/fukidashi" />
<ImageButton
android:id="@+id/userButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="80dp"
android:background="@color/background_transparent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/chatListButton"
app:srcCompat="@drawable/person" />
<View
android:id="@+id/footerBorder"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginBottom="72dp"
android:background="@color/background_border"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Chat list It is a layout for one line.
chat_list_row.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:id="@+id/chatListCardView"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:cardCornerRadius="15dp"
app:cardElevation="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/profileImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/ic_launcher" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:text="name"
android:textColor="@color/font_color_black"
android:textSize="12sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@+id/chatListCardView"
app:layout_constraintTop_toTopOf="@+id/chatListCardView" />
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="time"
android:textColor="@color/font_color_black"
android:textSize="8sp"
app:layout_constraintBottom_toBottomOf="@+id/name"
app:layout_constraintStart_toEndOf="@+id/name" />
<TextView
android:id="@+id/text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
android:text="text"
android:textColor="@color/font_color_black"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/name"
app:layout_constraintTop_toBottomOf="@+id/name" />
<View
android:id="@+id/line"
android:layout_width="match_parent"
android:layout_height="0.3dp"
android:layout_marginTop="8dp"
android:background="@color/background_border"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/chatListCardView" />
</androidx.constraintlayout.widget.ConstraintLayout>
values This is the constant file to use
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--Font color black-->
<color name="font_color_black">#5E5E5E</color>
<!--Font orange-->
<color name="font_color_orange">#FF9900</color>
<!-- background body -->
<color name="background_light_gray">#EBEBEB</color>
<!-- background header, footer -->
<color name="background_dark_gray">#E5E5E5</color>
<!--background separator-->
<color name="background_border">#838383</color>
<!--background transparent-->
<color name="background_transparent">#00E5E5E5</color>
</resources>
strings.xml
<?xml version="1.0" encoding="utf-8"?>
<!--Constant management file-->
<resources>
<!--app name-->
<string name="app_name">Chat</string>
<!--Chat list screen-->
<string name="chat_list_subject_label">CHATS</string>
<string name="chat_list_delete_button_color">#FF9900</string>
<string name="chat_list_delete_button_label">DELETE</string>
<string name="delete_dialog_message">Even if you delete this chat history\n Are you sure?</string>
<string name="delete_dialog_list_tag">chatList</string>
</resources>
styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorAccent">@color/font_color_orange</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
</resources>
I think you can reproduce the movement of the sample at the beginning with the above code. The source is also available here, so please check it if you like. https://github.com/yuta-matsumoto/chat
I wrote a hands-on article for the first time, but I'm very confused about how much I should explain. .. I would be very happy if anyone could help!
Recommended Posts