如何为android recyclerview中的最大字符串设置textview高度?

Posted

技术标签:

【中文标题】如何为android recyclerview中的最大字符串设置textview高度?【英文标题】:How to set textview height for the largest string in android recyclerview? 【发布时间】:2021-07-27 12:41:04 【问题描述】:

我有一个水平回收视图和适配器,在该图像内,然后在标题下方,然后在下方描述。现在所有元素的图像和标题长度几乎相同,但描述可能会有所不同。所有数据均来自 API。

现在我要做的是,我想设置所有 textview-description 元素的高度,其中描述长度的数量最多。

类似这个问题:How to set recycler height to highest item in recyclerView?

但我的问题是 diff,我只是在谈论 desc textview 而不是整个 recycleeview。

请注意:由于 desc 较小,可以在空白区域下方显示一个 cardview,但请确保 desc 应覆盖在具有最长字符串长度的 cardview 中。

代码:

public class RecommendationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> 
    private final int VIEW_TYPE = 1;
    private final Context mContext;
    private final ArrayList<Attributes> attributesArrayList;
    private final Resources mResources;
    private final BaseActivity baseActivity;
    private final OnItemClickListener listener;

    View.OnClickListener onClickListener = new View.OnClickListener() 
        @Override
        public void onClick(View v) 
            listener.onViewItemClicked();
        
    ;

    public RecommendationAdapter(Context mContext, ArrayList<BraAttributes> attributesArrayList, OnItemClickListener listener) 
        this.mContext = mContext;
        this.attributesArrayList = attributesArrayList;
        mResources = mContext.getResources();
        baseActivity = (BaseActivity) mContext;
        this.listener = listener;
    


    @NotNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NotNull ViewGroup parent, int viewType) 
        View itemLayoutView;
        if (viewType == VIEW_TYPE) 
            itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.recommend_item_layout, null);
            return new ChildViewHolderItem(itemLayoutView);
        
        throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
    

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewholder, int position) 
        if (getItemViewType(position) == VIEW_TYPE) 
            ChildViewHolderItem holder = (ChildViewHolderItem) viewholder;
            final Attributes attributes = attributesArrayList.get(position);
            holder.ctvTitle.setText(attributes.getName());
            holder.ctvDescription.setText(attributes.getMessage());
            setImage(attributes, holder);
        
    

    private void findLargestString(ArrayList<attributes> attributesArrayList, CustomTextView textView) 
        String res = "";
        int largestString = attributesArrayList.get(0).getMessage().length();

        for (int i = 0; i < attributesArrayList.size(); i++) 
            if (attributesArrayList.get(i).getMessage().length() > largestString) 
                largestString = attributesArrayList.get(i).getMessage().length();
                res = attributesArrayList.get(i).getMessage();
            
        
        LogUtils.d("Largest String: " + res);
        setTextViewHeight(textView, res);
    

    private void setImage(Attributes attributes, ChildViewHolderItem holder) 
        if (attributes.getImage().length() > 0) 
            Glide.with(holder.ivImage.getContext())
                    .load(IMAGE_SERVER_URL + "/medium.jpg")
                    .apply(new RequestOptions().error(R.drawable.default_load_image)
                            .placeholder(R.drawable.default_load_image))
                    .into(holder.ivImage);
        
    

    private int getHeight(Context context, CharSequence text, int textSize, int deviceWidth, Typeface typeface, int padding) 
        CustomTextView textView = new CustomTextView(context);
        textView.setPadding(padding, 0, padding, 0);
        textView.setTypeface(typeface);
        textView.setText(text, TextView.BufferType.SPANNABLE);
        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize);
        int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(deviceWidth, View.MeasureSpec.AT_MOST);
        int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        textView.measure(widthMeasureSpec, heightMeasureSpec);
        return textView.getMeasuredHeight();
    

    private void setTextViewHeight(CustomTextView ctvDescription, String largestString) 
        int deviceWidth;
        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(displayMetrics);
        deviceWidth = displayMetrics.widthPixels;
        LogUtils.d("deviceWidth: " + deviceWidth); //1080

        int padding = mContext.getResources().getDimensionPixelSize(R.dimen.dimen_15);

        int height = getHeight(mContext, largestString,
                mContext.getResources().getDimensionPixelSize(R.dimen.textsize_12),
                deviceWidth, Typeface.DEFAULT, padding);

        ctvDescription.setHeight(AppUtils.pxToDp(mContext, height));
    

    @Override
    public int getItemCount() 
        return Math.max(attributesArrayList.size(), 0);
    

    @Override
    public int getItemViewType(int position) 
        return VIEW_TYPE;
    

    public class ChildViewHolderItem extends RecyclerView.ViewHolder 
        @BindView(R.id.ivImage)
        AppCompatImageView ivImage;
        @BindView(R.id.ctvTitle)
        CustomTextView ctvTitle;
        @BindView(R.id.ctvDescription)
        CustomTextView ctvDescription;


        public ChildViewHolderItem(View v) 
            super(v);
            ButterKnife.bind(this, v);
            v.setOnClickListener(onClickListener);
            findLargestString(attributesArrayList, ctvDescription);
        
    


在上面的代码中,描述是从底部开始的!

【问题讨论】:

您的 Description textView 是否有最大行属性 Set ?我猜应该是因为你不能在 Recycler 视图项中显示 1000 行描述。如果是,那么您可以将 android:lines 属性添加到您的 TextView @ADM 我认为你没有明白我的意思。首先,没有设置最大属性,第二,我想显示完整的描述,第三,我不能给出包装内容,因为一个卡片视图会显示小而另一个卡片视图会因为描述长度而显示更大,它可能会有所不同。第 4 条描述线最多为 2-3 行。第五,我不想硬核尺寸,因为它适用于各种设备。我已经在我的问题中说过,我的问题类似于:***.com/questions/57890199/… 我认为你最好设置一个默认大小并显示一些描述而不是全部。如果您添加了一个功能来调整所有视图的大小,那么您正在创建 unnessacry 运动。应该使用动作来吸引用户对重要事件的注意。 这种调整大小只会发生在较小的旧视图上,而不会发生在新视图上,因为它们将直接使用较大尺寸的视图,并且根本不会调整大小。我建议您至少尝试一次,如果您需要进一步的指导,请告诉我。 @ADM 请检查已编辑的问题,我添加了代码。 【参考方案1】:

更新

更新记者请求的答案,因为她希望根据最大描述以相同高度和最高高度显示所有视图。

我已经在代码中做了几个 tweeks。

创建了一个方法来计算 textView 的投影高度,方法是尝试列表中的所有描述以获得最高高度。

 public static int getHeightOfLargestDescription(final Context context, final CharSequence text, TextView textView) 
    final WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    final Point displaySize = new Point();
    wm.getDefaultDisplay().getSize(displaySize);
    final int deviceWidth = displaySize.x;
    textView.setTypeface(Typeface.DEFAULT);
    textView.setText(text, TextView.BufferType.SPANNABLE);
    int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(deviceWidth, View.MeasureSpec.AT_MOST);
    int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    textView.measure(widthMeasureSpec, heightMeasureSpec);
    return textView.getMeasuredHeight();

然后使用此方法在onCreateViewHolder 中准备好在绑定视图时使用的最高高度。

        MyViewHolder myViewHolder = new MyViewHolder(itemView);
    for (Model m : modelList) 
        currentItemHeight = getHeightOfLargestDescription(context, m.description, myViewHolder.description);
        if (currentItemHeight > highestHeight) 
            highestHeight = currentItemHeight;
        
    

然后在onBindViewHolder`中使用这个highestHeight来设置描述TexView的高度,这样所有的视图总是有相同的高度,等于最高的高度。

 viewHolder.description.setHeight(highestHeight);

代码在同一个仓库中提交

以前的解决方案 我已经为您的问题创建了解决方案并在 GitHub 中提交 https://github.com/dk19121991/HorizontalRecyclerWithDynamicHeight

让我分享一下解决方案的要点

您可以在BindViewHolder 上获取描述视图的高度并将其存储以检查该高度是否最高,并始终将新视图的高度设置为您迄今为止获得的最高高度。

viewHolder.description.post(new Runnable() 
            @Override
            public void run() 
                currentItemHeight = viewHolder.description.getMeasuredHeight();
                if (currentItemHeight > highestHeight) 
                    highestHeight = currentItemHeight;
                    /** this is needed to make sure already inflated view with small description also get resized to largest view till now
                     *and this be called only if the new view is of larger size then all of the view inflated till now
                     */
                    notifyDataSetChanged();
                
                viewHolder.description.setHeight(highestHeight);
            
        );

您可能会问,如果已创建的旧视图较小,而要处理的新视图由于描述较大而较大,该怎么办。

完全不用担心。为了解决这个问题,我在遇到新的最高高度时添加了notifyDataSetChanged();,这将确保所有旧视图也获得新的更大高度。

点击这里查看应用程序的运行情况 https://github.com/dk19121991/HorizontalRecyclerWithDynamicHeight/blob/master/runningSample.gif

如果这能解决您的问题,请告诉我,如果您还有其他问题,请随时提出。

谢谢

【讨论】:

这是一个有趣的解决方案@dinkar_kumar,我很想知道您将如何处理从回收器中删除的最大视图,因此较小的剩余视图没有一堆死空间。 为此,我们会将最高高度作为全局变量存储在适配器中。 我可能没有很好地解释我的问题。一旦将视图加载到回收器中,您的 git 就会设置最高值,我很好奇您是否设置了最高值,并且如果您可以将较低的值设置为最高值以避免死区,则视图会从回收器中删除。 如果视图被删除,那么它的死空间怎么来的,recyclerview 已经通过重用视图来处理这些事情,我想你把它和 listview 混淆了。我在这个例子中使用了 recyclerview。 我指的是 RecyclerView。现在我认为我的问题有点毫无意义,您只需将内容包装在项目 XML 上以调整大小。

以上是关于如何为android recyclerview中的最大字符串设置textview高度?的主要内容,如果未能解决你的问题,请参考以下文章

XML/android:如何为 RecyclerView 项目制作白色下划线? [复制]

android中RecyclerView嵌套RecyclerView中,如何为内层RecyclerView设值的问题

如何为无限滚动的 RecyclerView 绘制背景

如何为 RecyclerView 设置 onItemClickListener? [复制]

滚动时如何为 recyclerview 项目设置动画?

如何为最后一个元素为 RecyclerView 添加边距?