[英]How can I make sticky headers in RecyclerView? (with or without external lib)
I'm using room db and I've a table from where I get a list of LiveData.我正在使用房间数据库,并且我有一张表格,可以从中获取 LiveData 列表。 In that table there is a column of Date where I store current date.
在该表中有一个 Date 列,我在其中存储当前日期。 Current date is selected by default, but user can also change the date when inserting data in the database.
默认选择当前日期,但用户也可以在向数据库中插入数据时更改日期。
I want to show this data in a recyclerview in this manner我想以这种方式在回收站视图中显示这些数据
https://imgur.com/RYegFpG
https://imgur.com/RYegFpG
I want to section this data according to the month and year as header and all entries of that month year below it.我想根据月份和年份将此数据划分为 header 以及该月份年份的所有条目。
For example, user inserted data in October 2019, I want this "October 2019" as a header in recyclerview and all entries of this month below it.例如,用户在 2019 年 10 月插入数据,我希望这个“2019 年 10 月”作为 recyclerview 中的 header 及其下方本月的所有条目。 Just like this all months entries should be shown with same manner as every next month becomes header and the entries of that month below it.
就像这样,所有月份的条目都应该以相同的方式显示,因为每个下个月变成 header 以及该月下面的条目。
I've tried to achieve this by doing我试图通过这样做来实现这一目标
if (!thisDate.equals(dBDate))
{
holder.transMonthWrapper.setVisibility(View.VISIBLE);
if (IEList.getType().equalsIgnoreCase("income"))
{
String amount = ""+IEList.getAmount();
holder.tvTransAmount.setText(amount);
holder.tvTransAmount.setTextColor(Color.GREEN);
holder.tvTransCategory.setText(IEList.getCategory());
holder.tvTransCategory.setTextColor(Color.GREEN);
}
else
{
String amount = ""+IEList.getAmount();
holder.tvTransAmount.setText(amount);
holder.tvTransAmount.setTextColor(Color.RED);
holder.tvTransCategory.setText(IEList.getCategory());
holder.tvTransCategory.setTextColor(Color.RED);
}
thisDate = dBDate;
holder.tvTransMonth.setText(thisDate);
}
else
{
holder.transMonthWrapper.setVisibility(View.GONE);
if (IEList.getType().equalsIgnoreCase("income"))
{
String amount = ""+IEList.getAmount();
holder.tvTransAmount.setText(amount);
holder.tvTransAmount.setTextColor(Color.GREEN);
holder.tvTransCategory.setText(IEList.getCategory());
holder.tvTransCategory.setTextColor(Color.GREEN);
}
else
{
String amount = ""+IEList.getAmount();
holder.tvTransAmount.setText(amount);
holder.tvTransAmount.setTextColor(Color.RED);
holder.tvTransCategory.setText(IEList.getCategory());
holder.tvTransCategory.setTextColor(Color.RED);
}
}
But the problem in this code is that when user change the month from settings and put some entries into database and that year's month entries are already present in the recyclerview.但是这段代码中的问题是,当用户从设置中更改月份并将一些条目放入数据库时,那一年的月份条目已经存在于 recyclerview 中。 It creates another header of that existing month in recyclerview.
它会在 recyclerview 中创建该现有月份的另一个 header。 But I want this to put those entries in existing month header, not to create new header of that month.
但我希望将这些条目放在现有月份 header 中,而不是创建该月的新 header。
What will be the best approach to achieve this without using external libraries, because I don't want to be dependent on external libraries in this case.在不使用外部库的情况下实现这一目标的最佳方法是什么,因为在这种情况下我不想依赖外部库。
I'm fairly new in programming.我在编程方面相当新。
Updated更新
In activity活动中
public void getTransactionData()
{
adapter = new TransactionAdapter();
recyclerView.setAdapter(adapter);
incomeExpenseModel = ViewModelProviders.of(AllTransaction.this).get(IncomeExpenseViewModel.class);
incomeExpenseModel.getIncomeExpenseData().observe(this, new Observer<List<IncomeExpense>>() {
@Override
public void onChanged(List<IncomeExpense> incomeExpenses) {
adapter.setIncomeExpenseList(incomeExpenses);
}
});
In recyclerAdapter在回收器适配器中
public void onBindViewHolder(@NonNull TransactionViewHolder holder, int position) {
IncomeExpense IEList = incomeExpenseList.get(position);
preferences = context.getSharedPreferences(settingPref, Context.MODE_PRIVATE);
String dateFormat = preferences.getString("Date_Format", "MM.dd.yy");
int lastIndex = incomeExpenseList.size() - 1;
IncomeExpense IELastIndex = incomeExpenseList.get(lastIndex);
String dateFrmDb= IELastIndex.getDate();
DateFormat df=new SimpleDateFormat(dateFormat);
Date d;
try {
d = df.parse(dateFrmDb);
df=new SimpleDateFormat("MMMM yyyy");
if (d != null) {
dBDate = df.format(d);
}
} catch (ParseException e) {
Toast.makeText(context, "Error" +e, Toast.LENGTH_SHORT).show();
}
if (!thisDate.equals(dBDate))
{
holder.transMonthWrapper.setVisibility(View.VISIBLE);
if (IEList.getType().equalsIgnoreCase("income"))
{
String amount = ""+IEList.getAmount();
holder.tvTransAmount.setText(amount);
holder.tvTransAmount.setTextColor(Color.GREEN);
holder.tvTransCategory.setText(IEList.getCategory());
holder.tvTransCategory.setTextColor(Color.GREEN);
}
else
{
String amount = ""+IEList.getAmount();
holder.tvTransAmount.setText(amount);
holder.tvTransAmount.setTextColor(Color.RED);
holder.tvTransCategory.setText(IEList.getCategory());
holder.tvTransCategory.setTextColor(Color.RED);
}
thisDate = dBDate;
holder.tvTransMonth.setText(thisDate);
}
else
{
holder.transMonthWrapper.setVisibility(View.GONE);
if (IEList.getType().equalsIgnoreCase("income"))
{
String amount = ""+IEList.getAmount();
holder.tvTransAmount.setText(amount);
holder.tvTransAmount.setTextColor(Color.GREEN);
holder.tvTransCategory.setText(IEList.getCategory());
holder.tvTransCategory.setTextColor(Color.GREEN);
}
else
{
String amount = ""+IEList.getAmount();
holder.tvTransAmount.setText(amount);
holder.tvTransAmount.setTextColor(Color.RED);
holder.tvTransCategory.setText(IEList.getCategory());
holder.tvTransCategory.setTextColor(Color.RED);
}
}
}
@Override
public int getItemCount() {
return incomeExpenseList.size();
}
public void setIncomeExpenseList(List<IncomeExpense> incomeExpenseList)
{
this.incomeExpenseList = incomeExpenseList;
notifyDataSetChanged();
}
You don't need to use any third party libraries.您不需要使用任何第三方库。 You can make use of
ExpandableListView
without actually making it "expand and collapse" to do the exact same thing which you need.您可以使用
ExpandableListView
而无需实际使其“展开和折叠”来执行您需要的完全相同的事情。 See my answer for this post . 请参阅我对这篇文章的回答。 The advantage here is that you can deal with this as easily as you deal with an
ExpandableListView
, with no custom code.这里的优点是您可以像处理
ExpandableListView
一样轻松地处理此问题,而无需自定义代码。 You only need to add one line to what is otherwise a standard ExpandableListView
adapter.您只需在标准的
ExpandableListView
适配器中添加一行。
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
...
((ExpandableListView) parent).expandGroup(groupPosition);
...
}
Your section headers, you can set as a group view and the list items as children.您可以将部分标题设置为组视图,并将列表项设置为子项。 Your data structure need to be updated before being passed on to the adapter.
在传递给适配器之前,您的数据结构需要更新。 It need to be a grouped data, not a plain list which you have to pass to the expandable adapter(for example an array of classes, each instance that contain a
String
property for group header and an ArrayList
of IncomeExpense
objects).它必须是分组数据,而不是必须传递给可扩展适配器的普通列表(例如,类数组,每个实例包含组 header 的
String
属性和收入IncomeExpense
ArrayList
。 And when you update the data, make the update in the corresponding group, instead of the entire data.并且当您更新数据时,在相应的组中进行更新,而不是整个数据。
Parent recyclerview父回收站view
<FrameLayout 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"
tools:context=".fragments.PhotoFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_photo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:clipToPadding="false"
android:paddingBottom="170dp">
</androidx.recyclerview.widget.RecyclerView>
</FrameLayout>
Its adapter它的适配器
public class PhotoAdapter extends RecyclerView.Adapter<PhotoAdapter.ViewHolder> {
private static final String TAG = "PhotoAdapter";
private Map<String, List<Photo>> photoMap;
private Context context;
private PhotoCategoryAdapter photoCategoryAdapter;
private List<String> keysList;
public PhotoAdapter(Map<String, List<Photo>> photoMap, Context context, List<String> keysList) {
this.photoMap = photoMap;
this.context = context;
this.keysList = keysList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.row_photo, parent, false));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
String date = keysList.get(position);
holder.lblTakenDate.setText(date);
List<Photo> photoList = photoMap.get(date);
String size = ("( "+ photoList.size() + " )");
holder.lblCountPhotos.setText(size);
setRecyclerView(holder, context, photoList);
}
@Override
public int getItemCount() {
return photoMap.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.rv_photo_category) RecyclerView recyclerView;
@BindView(R.id.lbl_taken_date_photo) TextView lblTakenDate;
@BindView(R.id.lbl_count_images_photo) TextView lblCountPhotos;
public ViewHolder(@NonNull View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
Reyclerview inside recyclerview Recyclerview里面的Reyclerview
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp">
<TextView
android:id="@+id/lbl_taken_date_photo"
android:textColor="@android:color/black"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:textSize="14dp"
android:hint="23-Aug-2018" />
<TextView
android:id="@+id/lbl_count_images_photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_toRightOf="@id/lbl_taken_date_photo"
android:hint="(2)"
android:textSize="12sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_photo_category"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/lbl_taken_date_photo"
android:layout_marginTop="10dp"
android:background="@color/light_grey" />
</RelativeLayout>
Its adapter它的适配器
public class PhotoCategoryAdapter extends RecyclerView.Adapter<PhotoCategoryAdapter.ViewHolder> {
private static final String TAG = "PhotoCategoryAdapter";
private List<Photo> photoList;
private Context context;
public PhotoCategoryAdapter(List<Photo> photoList, Context context) {
this.photoList = photoList;
this.context = context;
}
@NonNull
@Override
public PhotoCategoryAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.row_category_photo, parent, false));
}
@Override
public void onBindViewHolder(@NonNull PhotoCategoryAdapter.ViewHolder holder, int position) {
Photo photo = photoList.get(position);
RequestOptions myOptions = new RequestOptions() .format(DecodeFormat.PREFER_ARGB_8888).
fitCenter().override(100, 100).placeholderOf(R.drawable.ic_image);
Glide.with(context)
.applyDefaultRequestOptions(myOptions)
.asBitmap()
.load(photo.getImage())
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.into(holder.imgVImages);
}
@Override
public int getItemCount() {
return photoList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.imgv_images_category_photo)
ImageView imgVImages;
@BindView(R.id.imgv_selected_icon_photo) ImageView imgVSelectedPhoto;
public ViewHolder(@NonNull View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
Row of inner recyclerview内部recyclerview的行
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_margin="5dp"
android:layout_width="80dp"
android:layout_height="80dp">
<ImageView
android:id="@+id/imgv_images_category_photo"
android:scaleType="fitXY"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/imgv_selected_icon_photo"
android:visibility="gone"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_margin="10dp"
android:src="@drawable/ic_select"
android:layout_alignParentEnd="true"/>
</RelativeLayout>
Activity or fragment from where you will send data to adapter将数据发送到适配器的活动或片段
public class PhotoFragment extends Fragment {
@BindView(R.id.rv_photo) RecyclerView recyclerView;
private PhotoAdapter photoAdapter;
private ArrayList<Photo> photoList;
private ArrayList<String> keyList;
private Map<String,List<Photo>> map;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_photo, container, false);
ButterKnife.bind(this, view);
init();
setRecyclerViewAdapter();
new PhotoAsync(getContext()).execute();
return view;
}
private void init(){
map = new HashMap<>();
keyList = new ArrayList<>();
photoList = new ArrayList<>();
}
//set layout manager to recyclerView
private void setRecyclerViewAdapter() {
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
}
//get list of images
@RequiresApi(api = Build.VERSION_CODES.Q)
private List<Photo> getAllImages(){
Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.DATE_TAKEN};
Cursor c = null;
ArrayList<Photo> photoList = new ArrayList<>();
if (u != null) {
c = getContext().getContentResolver().query(u, projection, null, null, null); }
if ((c != null) && (c.moveToFirst())) {
do {
Photo photo = new Photo();
String path = c.getString(c.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DATA));
String takenDate = c.getString(c.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DATE_TAKEN));
long millisecond = Long.parseLong(takenDate);
String date = DateFormat.format("dd-MMM-yyyy", new Date(millisecond)).toString();
try{
photo.setImage(path);
photo.setDate(date);
photoList.add(photo);
}
catch(Exception e)
{ }
}
while (c.moveToNext());
}
//reverse photoList
Collections.reverse(photoList);
return photoList;
}
public class PhotoAsync extends AsyncTask<Void, Void, Void> {
private Context context;
public PhotoAsync(Context context) {
this.context = context;
}
@RequiresApi(api = Build.VERSION_CODES.Q)
@Override
protected Void doInBackground(Void... params) {
getPhotoList();
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
photoAdapter = new PhotoAdapter(map, context, keyList);
recyclerView.setAdapter(photoAdapter);
photoAdapter.notifyDataSetChanged();
}
@Override
protected void onPreExecute() {
}
}
@RequiresApi(api = Build.VERSION_CODES.Q)
private void getPhotoList(){
photoList.addAll(getAllImages());
for (Photo string : photoList) {
if (!keyList.contains(string.getDate()))
keyList.add(string.getDate());
else;
}
for (String s : keyList){
ArrayList<Photo> photos = new ArrayList<>();
for (Photo s1 : photoList) {
if (s1.getDate().equals(s))
photos.add(s1);
else;
}
map.put(s, photos);
}
}
}
Here is the logic which you have to apply
1. Find all the common dates from the data list
for example you data list is :
List<Model> dataList = new ArrayList<>();
dataList.add(new Model("23/10/19"));
dataList.add(new Model("23/10/19"));
dataList.add(new Model("23/09/19"));
dataList.add(new Model("23/10/19"));
dataList.add(new Model("27/09/19"));
dataList.add(new Model("23/10/19"));
List<String> commonList = new ArrayList<>();
for(Model m : dataList){
if(!commonList.contains(model.getDate()))
commonList.add(m.getDate());
else
Log.d("Dates", commonList);
}
Above function will help in getting all common dates
//Here map store date with data which has common date
Map<String, List<Model>> map = new HashMap<>();
List<Model> objectsofCommonDate = new ArrayList();
for(String date: commonList){
objectsofCommonDate.clear();
for(Model model : dataList){
if(model.getData.contains(date))
objectsofCommonDate.add(model);
else
\\do nothing
}
map.put(data, objectsOfCommonDate);
}
and pass map to main recyclerview adapter along with commonDateList;
Change notifyDataSetChanged() to notify() or notifyAll().将 notifyDataSetChanged() 更改为 notify() 或 notifyAll()。
public void setIncomeExpenseList(List<IncomeExpense> incomeExpenseList) { this.incomeExpenceList.clear(); this.incomeExpenseList = incomeExpenseList; notify(); }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.