如何在每个子类中显示不同的片段/视图?Android

huangapple go评论75阅读模式
英文:

How is it possible to display different fragment/view for each subclass? Android

问题

我有一个Message对象列表。Message是一个抽象类,有两个子类:TextMessageImageMessage

我想要在可滚动的列表中显示这些消息,根据消息的类型进行显示。如何创建一个自定义的视图/片段,以抽象类作为参数,并根据实际的子类创建其中的TextView/ImageView?

我已阅读了官方的Android指南,但仍然不知道如何实现这一点。

英文:

I have a list of Message objects. Message is an abstract class, with two subclasses: TextMessage and ImageMessage

I would like to display the messages in a scrollable list, based on the type of the message. How is it possible to create a custom view/fragment with an abstract class as parameter, and create a TextView/ImageView inside it according to the actual subclass?

I've read the official android guide, but I still have no idea, how to do this.

答案1

得分: 2

好的,以下是翻译好的内容:

好的,既然 RecyclerView 是推荐用于显示具有未定义大小的项目列表的视图,我会假设你想要在这里使用 RecyclerView。

如果你按照 示例代码 来创建这样一个视图,那么你可以让你的 Message 类声明一个抽象的 getViewTypebindToView 方法,如下所示:

Message.java

public abstract class Message {
    public abstract int getViewType();
    public abstract void bindToView(View messageView);

    public enum TYPE {
        TextMessage,
        ImageMessage
    }

    public static View createView(int typeOrdinal, Context context) {
        switch (TYPE.values()[typeOrdinal]) {
            case TextMessage:
                return TextMessage.createNewView(context);
            case ImageMessage:
                return ImageMessage.createNewView(context);
            default:
                throw new RuntimeException("Incorrect typeOrdinal: " + typeOrdinal);
        }
    }

}

TextMessage.java

public class TextMessage extends Message {

    public static TextView createNewView(Context context) {
        return new TextView(context);
    }

    @Override
    public void bindToView(View messageView) {
        ((TextView) messageView).setText("一些特定的文本");
    }

    @Override
    int getViewType() {
        return TYPE.TextMessage.ordinal();
    }
}

ImageMessage.java

public class ImageMessage extends Message {
    public static ImageView createNewView(Context context) {
        return new ImageView(context);
    }

    @Override
    public void bindToView(View messageView) {
        Bitmap forExampleSomeBitmap = null; // TODO 实现
        ((ImageView) messageView).setImageBitmap(forExampleSomeBitmap);
    }

    @Override
    int getViewType() {
        return TYPE.ImageMessage.ordinal();
    }
}

然后,保持来自该 Google 示例代码的所有其余代码不变,你的 RecyclerView.Adapter 可能会如下所示:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    private Message[] mDataset;

    public static class MyViewHolder extends RecyclerView.ViewHolder {
        public View messageView;

        public MyViewHolder(View v) {
            super(v);
            messageView = v;
        }
    }

    @NonNull
    @Override
    public MyAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new MyViewHolder(Message.createView(viewType, parent.getContext()));
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        mDataset[position].bindToView(holder.messageView);
    }

    @Override
    public int getItemViewType(int position) {
        return mDataset[position].getViewType();
    }

    @Override
    public int getItemCount() {
        return mDataset.length;
    }
}

如果你想了解更多关于 RecyclerView 如何工作以及为什么它会回收视图的信息,我建议阅读 这篇文章

英文:

Ok, So since a RecyclerView is the recommended view for a list of items with an undefined size, I'm gonna assume your want to use a RecyclerView for this.

If you then follow the sample code to make such a view, then you could make your Message class declare an abstract getViewType and bindToView method as follows:

Message.java

public abstract class Message {
    public abstract int getViewType();
    public abstract void bindToView(View messageView);

    public enum TYPE {
        TextMessage,
        ImageMessage
    }

    public static View createView(int typeOrdinal, Context context) {
        switch (TYPE.values()[typeOrdinal]) {
            case TextMessage:
                return TextMessage.createNewView(context);
            case ImageMessage:
                return ImageMessage.createNewView(context);
            default:
                throw new RuntimeException(&quot;Incorrect typeOrdinal: &quot; + typeOrdinal);
        }
    }

}

TextMessage.java

public class TextMessage extends Message {

    public static TextView createNewView(Context context) {
        return new TextView(context);
    }

    @Override
    public void bindToView(View messageView) {
        ((TextView) messageView).setText(&quot;some specific text&quot;);
    }

    @Override
    int getViewType() {
        return TYPE.TextMessage.ordinal();
    }
}

ImageMessage.java

public class ImageMessage extends Message {
    public static ImageView createNewView(Context context) {
        return new ImageView(context);
    }

    @Override
    public void bindToView(View messageView) {
        Bitmap forExampleSomeBitmap = null; // TODO implement
        ((ImageView) messageView).setImageBitmap(forExampleSomeBitmap);
    }

    @Override
    int getViewType() {
        return TYPE.ImageMessage.ordinal();
    }
}

And then With all the rest of the code from that google sample code unchanged, your RecyclerView.Adapter could then look like this:

public class MyAdapter extends RecyclerView.Adapter&lt;MyAdapter.MyViewHolder&gt; {
    private Message[] mDataset;

    public static class MyViewHolder extends RecyclerView.ViewHolder {
        public View messageView;

        public MyViewHolder(View v) {
            super(v);
            messageView = v;
        }
    }

    @NonNull
    @Override
    public MyAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new MyViewHolder(Message.createView(viewType, parent.getContext()));
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        mDataset[position].bindToView(holder.messageView);
    }

    @Override
    public int getItemViewType(int position) {
        return mDataset[position].getViewType();
    }

    @Override
    public int getItemCount() {
        return mDataset.length;
    }
}

If you want to know more about how the recyclerview works, why it recycles views, then I would recommend this arcticle.

答案2

得分: 1

1: 创建新项目 -> 选择 基本活动

在您的活动内有两个片段和一个按钮,用于在片段 A 和 B 之间进行切换。

2: 在 SecondFragment(片段 B)中创建一个 RecyclerView 资源

将以下行添加到第二个片段:

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerview"
    android:layout_width="409dp"
    android:layout_height="460dp"
    android:layout_marginStart="1dp"
    android:layout_marginTop="1dp"
    android:layout_marginEnd="1dp"
    android:layout_marginBottom="1dp"
    android:visibility="visible"
    app:layout_constraintBottom_toTopOf="@+id/button_second"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

3: 创建一个带有 TextView 的帧布局

前往 /res/layout/newlayout/layoutresource 文件,创建一个帧布局,将以下代码替换到布局内:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="8dp">

    <TextView
        android:id="@+id/randomText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:text="This is some temp text" />

</FrameLayout>

4: 在片段中添加 RecyclerView

前往第二个片段,粘贴以下所有代码:

public class SecondFragment extends Fragment {

    // 添加 RecyclerView 成员
    private RecyclerView recyclerView;

    @Override
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState
    ) {

        // 为该片段填充布局
        View view = inflater.inflate(R.layout.fragment_second, container, false);

        // 添加以下行以创建 RecyclerView
        recyclerView = view.findViewById(R.id.recyclerview);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));
        recyclerView.setAdapter(new RandomNumListAdapter(1234));

        return view;
    }
}

5: 为 RecyclerView 创建一个 ViewHolder

创建如下类:

public class RecyclerViewHolder extends RecyclerView.ViewHolder {

    private TextView view;

    public RecyclerViewHolder(@NonNull View itemView) {
        super(itemView);
        view = itemView.findViewById(R.id.randomText);
    }

    public TextView getView() {
        return view;
    }
}

6: 创建一个 ListAdapter

public class RandomNumListAdapter extends RecyclerView.Adapter<RecyclerViewHolder> {

    private Random random;

    public RandomNumListAdapter(int seed) {
        this.random = new Random(seed);
    }

    @Override
    public int getItemViewType(final int position) {
        return R.layout.frame_textview;
    }

    @NonNull
    @Override
    public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
        return new RecyclerViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {
        holder.getView().setText(String.valueOf(random.nextInt()));
    }

    @Override
    public int getItemCount() {
        return 100;
    }
}

您可以用随机数字替换这些项。

英文:

1 : Create new project -> select Basic Activity

there is to fragment inside your activity and a button to switch between fragment A to B

2 : create a RecyclerView resource in SecondFragment (fragment B )

add the following line into second fragment :

&lt;androidx.recyclerview.widget.RecyclerView
    android:id=&quot;@+id/recyclerview&quot;
    android:layout_width=&quot;409dp&quot;
    android:layout_height=&quot;460dp&quot;
    android:layout_marginStart=&quot;1dp&quot;
    android:layout_marginTop=&quot;1dp&quot;
    android:layout_marginEnd=&quot;1dp&quot;
    android:layout_marginBottom=&quot;1dp&quot;
    android:visibility=&quot;visible&quot;
    app:layout_constraintBottom_toTopOf=&quot;@+id/button_second&quot;
    app:layout_constraintEnd_toEndOf=&quot;parent&quot;
    app:layout_constraintStart_toStartOf=&quot;parent&quot;
    app:layout_constraintTop_toTopOf=&quot;parent&quot; /&gt;

3 : create a frame layout with textview
go to /res/layout/newlayout/layoutresource file and create an framlayout
inside the layout replace this codes

&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;FrameLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;wrap_content&quot;
    android:layout_height=&quot;wrap_content&quot;
    android:layout_margin=&quot;8dp&quot;&gt;

    &lt;TextView
        android:id=&quot;@+id/randomText&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        tools:text=&quot;This is some temp text&quot; /&gt;

&lt;/FrameLayout&gt;

4: Add RecyclerView in Fragment
go to second fragment and paste all of these

public class SecondFragment extends Fragment {

    // Add RecyclerView member
    private RecyclerView recyclerView;

    @Override
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState
    ) {

        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_second, container, false);

        // Add the following lines to create RecyclerView
        recyclerView = view.findViewById(R.id.recyclerview);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));
        recyclerView.setAdapter(new RandomNumListAdapter(1234));

        return view;
    }

5 : create a class Viewholder for recyclerview
like this :

public class RecyclerViewHolder extends RecyclerView.ViewHolder {

    private TextView view;
    public RecyclerViewHolder(@NonNull View itemView) {
        super(itemView);
        view = itemView.findViewById(R.id.randomText);
    }

    public TextView getView(){
        return view;
    }
}

6 : Create ListAdapter

public class RandomNumListAdapter extends RecyclerView.Adapter&lt;RecyclerViewHolder&gt; {
    private Random random;

    public RandomNumListAdapter(int seed) {
        this.random = new Random(seed);
    }

    @Override
    public int getItemViewType(final int position) {
        return R.layout.frame_textview;
    }

    @NonNull
    @Override
    public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

        View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
        return new RecyclerViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {
        holder.getView().setText(String.valueOf(random.nextInt()));
    }

    @Override
    public int getItemCount() {
        return 100;
    }
}

you can replace your items with random numbers .

huangapple
  • 本文由 发表于 2020年8月30日 02:38:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/63650553.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定