使用 multiView RecyclerView 将两行布局彼此放置时出现问题?包括截图

Problem in positioning two row layouts one below each other with multiView RecyclerView? Screenshot included

这里我使用了两个 viewHolder,一个用于 NoteViewHolder,另一个用于 ChecklistViewHolder,用于单个适配器。我在 recyclerView 适配器中有两个具有不同数据类型的列表。我使用 SQLite 数据库并创建了两个表,一个用于笔记,另一个用于清单,因为我为 recyclerView 中的每一行布局使用了不同的视图。我想要的是根据它们的添加方式定位注释和清单。 这就是我在 RecyclerView 中得到的。

在这里,如果我添加另一个注释,它会转到黄色部分的注释部分,但我希望它转到紫色部分的清单之后。

我知道如何绑定这两个视图,绑定没有问题。我的问题是你的布局。

适配器class

    public class AdapterClass extends RecyclerView.Adapter {

    List<NoteHelper> notesList;
    List<ChecklistHelper> checklistsList;
    public static final int LAYOUT_ONE = 0;
    public static final int LAYOUT_TWO = 1;


    public AdapterClass(List<NoteHelper> notesList, List<ChecklistHelper> checklistHelperList) {
        this.notesList = notesList;
        this.checklistsList = checklistHelperList;
    }


    @Override
    public int getItemViewType(int position) {
        if (position<notesList.size())
            return LAYOUT_ONE;

       else return  LAYOUT_TWO;


    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        if(viewType==LAYOUT_ONE){
            
            View view = layoutInflater.inflate(R.layout.layout_note, parent,false);
            NoteViewHolder noteViewHolder = new NoteViewHolder(view);
            return noteViewHolder;
        }

        if(viewType==LAYOUT_TWO){
            View view = layoutInflater.inflate(R.layout.layout_checklist, parent, false);
            return new ChecklistViewHolder(view);
        }
        return null;
    }
    
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

    }
    
    @Override
    public int getItemCount() {
        return notesList.size() + checklistsList.size();
    }

    class NoteViewHolder extends RecyclerView.ViewHolder{
        public NoteViewHolder(@NonNull View itemView) {
            super(itemView);
        }
    }

    class ChecklistViewHolder extends RecyclerView.ViewHolder{
        public ChecklistViewHolder(@NonNull View itemView) {
            super(itemView);
        }
   }}

Activity class

public class MainActivity extends AppCompatActivity {

    RecyclerView recyclerView;
    Button btnNote, btnChecklist;
    List<NoteHelper> listNotes;
    List<ChecklistHelper> checklistHelperList;
    DbHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.recycler_view);
        btnNote = findViewById(R.id.btn_add_note);
        btnChecklist = findViewById(R.id.btn_add_checklist);
        recyclerView = findViewById(R.id.recycler_view);
        dbHelper = new DbHelper(this);

        listNotes = dbHelper.getNotes();
        checklistHelperList = dbHelper.getChecklists();
        
        AdapterClass adapterClass = new AdapterClass(listNotes, checklistHelperList);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapterClass);
        listNotes = new ArrayList<>();

        btnNote.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this,activity_add_note.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(intent);

            }
        });

        btnChecklist.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, activity_add_checklist.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(intent);

            }
        });
    }}
public class activity_add_note extends AppCompatActivity {
Button btnSaveNote;
EditText etNote;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_add_note);

    btnSaveNote = findViewById(R.id.btn_save_note);
    etNote = findViewById(R.id.et_note);

    btnSaveNote.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            DbHelper  dbHelper = new DbHelper(getApplicationContext());
            dbHelper.getWritableDatabase();
            String note = etNote.getText().toString();
            dbHelper.insertNote(note);

            Intent intent = new Intent(getApplicationContext(), MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);
        }
    });
}}

    public class activity_add_checklist extends AppCompatActivity {
    Button btnSaveChecklist;
    EditText etChecklist;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_checklist);

        btnSaveChecklist = findViewById(R.id.btn_save_checklist);
        etChecklist = findViewById(R.id.et_checklist);
        btnSaveChecklist.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String checklist = etChecklist.getText().toString();
                DbHelper dbHelper = new DbHelper(getApplicationContext());
                dbHelper.insertChecklist(checklist);

                Intent intent = new Intent(getApplicationContext(), MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(intent);
            }
        });
    }
}

修改了您的代码并进行了一些改进,以便为我们提供预期的结果。让我们一步一步地看看发生了什么变化以及为什么。

物品设计

这里的目标是将两个不同的 classes 合并到一个列表中。为了使这成为可能,我们需要添加一个 BaseItem class,它将包含 NoteHelperChecklistHelper classes 的所有通用属性和函数,以便我们从 BaseItem class.Note 扩展它们,它有一个名为 getViewType 的抽象方法,以便继承的 classes 可以实现此方法并为我们提供它们的视图类型。

BaseItem.java

public abstract class BaseItem {

    int id;
    String content;
    long date;

    public BaseItem() {
        content = "";
    }

    public BaseItem(int id, String content, long date) {
        this.id = id;
        this.content = content;
        this.date = date;
    }

    public abstract int getViewType();

    public int getId() {
        return id;
    }

    public String getContent() {
        return content;
    }
}

现在如果我们看到 NoteHelperChecklistHelper classes;每个 class 都有自己的视图类型定义,并实现了 getViewType class,我们将在适配器 class.

中方便地使用它

NoteHelper.java

public class NoteHelper extends BaseItem {

    public static final int VIEW_TYPE = 0;


    public NoteHelper() {
    }

    public NoteHelper(int id, String content, long date) {
        super(id, content, date);
    }


    @Override
    public int getViewType() {
        return VIEW_TYPE;
    }
}

ChecklistHelper.java

public class ChecklistHelper extends BaseItem {

    public static final int VIEW_TYPE = 1;

    public ChecklistHelper(){

    }

    public ChecklistHelper(int id, String content, long date){
        super(id, content, date);
    }

    @Override
    public int getViewType() {
        return VIEW_TYPE;
    }

}

将两项合并到一个列表中

下一步是设计组合逻辑。例如,我根据创建日期设计了组合。但它可以基于与此类似的任何标准来完成。为了实现基于日期的组合逻辑,我们需要一个长型比较器,因为我们使用 System.currentMillis().

将创建日期保存为时间戳

ItemLongDateComparator.java

public class ItemLongDateComparator implements Comparator<BaseItem> {
    @Override
    public int compare(BaseItem baseItem, BaseItem t1) {
        return Long.compare(baseItem.date, t1.date);
    }
}

重新设计数据库助手

在主线程中进行数据库查询不是一个好的做法,因为随着数据变大,它可能会导致 UI 故障。为了防止 UI 问题,我们使用执行程序服务在后台线程中执行查询,并将结果传递给主线程。但是在我们传递结果之前,我们通过创建时间戳标准在后台线程中组合数据,然后我们使用接口将结果发送到主线程。

DbHelper.java

public class DbHelper extends SQLiteOpenHelper {

    // This background executor is only for database operations
    private final Executor dbexecutor;

    public static final String DATABASE_NAME = "NOTES.DB";
    public static final int DATABASE_VERSION = 1;

    public static final String NOTES_TABLE = "NOTES_TABLE";
    public static final String CHECKLIST_TABLE = "CHECKLIST_TABLE";

    public static final String COLUMN_ID = "ID";
    public static final String COLUMN_CONTENT = "CONTENT";
    public static final String COLUMN_DATE = "DATE";


    public DbHelper(@Nullable Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        dbexecutor = Executors.newSingleThreadExecutor();
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {

        String query = " CREATE TABLE " + NOTES_TABLE + " ( " + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT ,"  +
                COLUMN_CONTENT + " TEXT, " + COLUMN_DATE + " DATE "  + " ) ";
            sqLiteDatabase.execSQL(query);

        String query2 = "CREATE TABLE " + CHECKLIST_TABLE + " ( " + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
                COLUMN_CONTENT + " TEXT, " + COLUMN_DATE + " DATE " + " ) ";
            sqLiteDatabase.execSQL(query2);
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {

        if (oldVersion>newVersion) {

            sqLiteDatabase.execSQL(" DROP TABLE IF EXISTS " + NOTES_TABLE);
            sqLiteDatabase.execSQL(" DROP TABLE IF EXISTS " + CHECKLIST_TABLE);
        }
             onCreate(sqLiteDatabase);

    }


    public void insertNote(String content, long date){
        dbexecutor.execute(()-> {
            SQLiteDatabase sqLiteDatabase;
            sqLiteDatabase = getWritableDatabase();

            ContentValues c = new ContentValues();
            c.put(COLUMN_CONTENT, content);
            c.put(COLUMN_DATE, date);
            sqLiteDatabase.insert(NOTES_TABLE, null, c);
        });
    }

    public void insertChecklist(String content, long date){
        dbexecutor.execute(()-> {
            SQLiteDatabase sqLiteDatabase;
            sqLiteDatabase = getWritableDatabase();

            ContentValues c = new ContentValues();
            c.put(COLUMN_CONTENT, content);
            c.put(COLUMN_DATE, date);
            sqLiteDatabase.insert(CHECKLIST_TABLE, null, c);
        });
    }


    private List<NoteHelper>  getNotes() {
        List<NoteHelper> notesList = new ArrayList<>();
        SQLiteDatabase sqLiteDatabase = this.getReadableDatabase();
        String query = " SELECT * FROM " + NOTES_TABLE;
        Cursor cursor = sqLiteDatabase.rawQuery(query, null);

        if (cursor.moveToFirst()) {

            do {
                int id = cursor.getInt(0);
                String content = cursor.getString(1);
                long date = cursor.getLong(2);
                NoteHelper noteHelper = new NoteHelper(id, content, date);
                notesList.add(noteHelper);
            }
            while (cursor.moveToNext());

        } else {
            sqLiteDatabase.close();
            cursor.close();
        }

        return notesList;
    }

    private List<ChecklistHelper> getChecklists(){
        List<ChecklistHelper> checklistsList = new ArrayList<>();
        SQLiteDatabase sqLiteDatabase = this.getReadableDatabase();
        String query = "SELECT * FROM " +  CHECKLIST_TABLE;
        Cursor cursor = sqLiteDatabase.rawQuery(query, null);

        if (cursor.moveToFirst()){

            do{
                int id = cursor.getInt(0);
                String content = cursor.getString(1);
                long date = cursor.getLong(2);
                ChecklistHelper checklistHelper = new ChecklistHelper(id, content, date);
                checklistsList.add(checklistHelper);

            }
            while (cursor.moveToNext());

        }

        else {
            cursor.close();
            sqLiteDatabase.close();
        }
        return checklistsList;
    }

    public void getNotesAndChecklists(OnContentReadyListener listener) {
        if(listener == null) return; // Don't proceed if the listener is null
        dbexecutor.execute(()-> {
            List<NoteHelper> notes = getNotes();
            List<ChecklistHelper> checks = getChecklists();
            // Merge lists and sort items by creation date
            List<BaseItem> items = new ArrayList<>();
            items.addAll(notes);
            items.addAll(checks);
            Collections.sort(items, new ItemLongDateComparator());
            new Handler(Looper.getMainLooper()).post(()-> listener.onContentReady(items));
        });
    }


    @MainThread
    public interface OnContentReadyListener {
        void onContentReady(List<BaseItem> combinedItems);
    }

}

将数据连接到 UI

现在我们已经完成了数据组织,我们可以轻松地使这个设计适应 UI API。在适配器 class 中,我们只使用 BaseItem 的列表 class 因为此列表将接受来自 BaseItem 的所有派生 classes。 只管理一个列表而不是两个列表会更容易。

AdapterClass.java

public class AdapterClass extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    List<BaseItem> itemsList;


    public AdapterClass() {
        itemsList = new ArrayList<>();
    }


    @Override
    public int getItemViewType(int position) {
        return itemsList.get(position).getViewType();
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        if(viewType == NoteHelper.VIEW_TYPE){

            View view = layoutInflater.inflate(R.layout.layout_note, parent,false);
            return new NoteViewHolder(view);
        }

        if(viewType == ChecklistHelper.VIEW_TYPE){
            View view = layoutInflater.inflate(R.layout.layout_checklist, parent, false);
            return new ChecklistViewHolder(view);
        }
        return null;
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        int vtype = getItemViewType(position);
        BaseItem bi = itemsList.get(position);
        if(vtype == NoteHelper.VIEW_TYPE) {
            NoteViewHolder nvh = (NoteViewHolder) holder;
            TextView tv = nvh.itemView.findViewById(R.id.tv_note);
            tv.setText(bi.getContent());
        }
        else if(vtype == ChecklistHelper.VIEW_TYPE) {
            ChecklistViewHolder cvh = (ChecklistViewHolder) holder;
            TextView tv = cvh.itemView.findViewById(R.id.tv_checklist);
            tv.setText(bi.getContent());

        }
    }

    @Override
    public int getItemCount() {
        return itemsList.size();
    }

    public void update(List<BaseItem> combinedList) {
        itemsList.clear();
        itemsList.addAll(combinedList);
        notifyItemRangeChanged(0, itemsList.size());
    }

    static class NoteViewHolder extends RecyclerView.ViewHolder{
        public NoteViewHolder(@NonNull View itemView) {
            super(itemView);
        }
    }

    static class ChecklistViewHolder extends RecyclerView.ViewHolder{
        public ChecklistViewHolder(@NonNull View itemView) {
            super(itemView);
        }
    }
}

将数据反映到 UI

终于可以将数据反映到UI部分了。还记得我提到过完成活动而不是一次又一次地重新启动主要 activity 吗?看看添加活动现在是多么简单。

activity_add_note.java

public class activity_add_note extends AppCompatActivity {
    Button btnSaveNote;
    EditText etNote;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_note);

        btnSaveNote = findViewById(R.id.btn_save_note);
        etNote = findViewById(R.id.et_note);

        btnSaveNote.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String note = etNote.getText().toString();
                DbHelper  dbHelper = new DbHelper(getApplicationContext());
                dbHelper.insertNote(note, System.currentTimeMillis());

                // Finish this activity
                new Handler(Looper.getMainLooper()).postDelayed(() -> finish(), 500);

            }
        });
    }
}

activity_add_checklist.java

public class activity_add_checklist extends AppCompatActivity {
    Button btnSaveChecklist;
    EditText etChecklist;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_checklist);

        btnSaveChecklist = findViewById(R.id.btn_save_checklist);
        etChecklist = findViewById(R.id.et_checklist);
        btnSaveChecklist.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String checklist = etChecklist.getText().toString();
                DbHelper dbHelper = new DbHelper(getApplicationContext());
                dbHelper.insertChecklist(checklist, System.currentTimeMillis());

                // Finish this activity
                new Handler(Looper.getMainLooper()).postDelayed(() -> finish(), 500);
            }
        });
    }
}

主要 activity 中需要注意的一件重要事情是,我们会在每次恢复时查询这些项目。因此,当我们 return 来自其他活动时,新添加的项目将出现在列表中。

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    RecyclerView recyclerView;
    AdapterClass adapterClass;
    Button btnNote, btnChecklist;
    DbHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.recycler_view);
        btnNote = findViewById(R.id.btn_add_note);
        btnChecklist = findViewById(R.id.btn_add_checklist);
        recyclerView = findViewById(R.id.recycler_view);
        dbHelper = new DbHelper(this);

        adapterClass = new AdapterClass();
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapterClass);

        btnNote.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this,activity_add_note.class);
                startActivity(intent);

            }
        });

        btnChecklist.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, activity_add_checklist.class);
                startActivity(intent);

            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume: ");
        if(dbHelper == null) {
            dbHelper = new DbHelper(this);
        }
        dbHelper.getNotesAndChecklists(new DbHelper.OnContentReadyListener() {
            @Override
            public void onContentReady(List<BaseItem> combinedItems) {
                adapterClass.update(combinedItems);
            }
        });
    }
}

获取整个项目:your app