无条件布局,来自视图适配器的膨胀:应使用View Holder模式

时间:2014-08-19 10:46:04

标签: java android layout-inflater

我在eclipse中收到以下警告:

  

来自视图适配器的无条件布局膨胀:应使用View Holder模式(使用传递给此方法的循环视图作为第二个参数)以实现更流畅的滚动。

在:

convertView = vi.inflate(R.layout.activity_friend_list_row, parent, false);

我有一个实现了CheckBox的基础适配器,我添加了一个标签以使CheckBox正常工作。

以下是代码:

 public View getView(final int position, View convertView, ViewGroup parent) 
  {

    ViewHolder mViewHolder;
    mViewHolder = new ViewHolder();
    LayoutInflater vi = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    convertView = vi.inflate(R.layout.activity_friend_list_row, parent, false);

    mViewHolder.cb = (CheckBox) convertView.findViewById(R.id.checkBox);

    convertView.setTag(mViewHolder);

    if (InviteFriends.isChecked[position] == true)
    {
        mViewHolder.cb.setChecked(true);
    }
    else
    {
        mViewHolder.cb.setChecked(false);
    }

    mViewHolder.cb.setOnCheckedChangeListener(new OnCheckedChangeListener() 
    {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean ischecked) 
        {
            if (buttonView.isChecked())
            {
                InviteFriends.isChecked[position] = true;

            }
            else
            {
                InviteFriends.isChecked[position] = false;
            }
        }
    });

    TextView friendsname  = (TextView) convertView.findViewById(R.id.friendsName); // title
    ImageView thumb_image = (ImageView) convertView.findViewById(R.id.list_image); // thumb image

    HashMap<String, String> song = new HashMap<String, String>();
    song = data.get(position);

    // Setting all values in listview
    friendsname.setText(song.get(InviteFriends.KEY_DISPLAY_NAME));
    imageLoader.DisplayImage(song.get(InviteFriends.KEY_IMAGEPROFILE_URL), thumb_image);


    return convertView;
}

结果正常。我该如何修复此警告?我无法为此获得解决方案吗?

谢谢!

5 个答案:

答案 0 :(得分:55)

试试这个

static class ViewHolder {

    private TextView friendsname;
    private ImageView thumb_image;
    private CheckBox cb;

}
public View getView(final int position, View convertView, ViewGroup parent) {

    ViewHolder mViewHolder = null;
    HashMap<String, String> song = null;

    if (convertView == null) {

        song = new HashMap <String, String>();
        mViewHolder = new ViewHolder();

        LayoutInflater vi = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        convertView = vi.inflate(R.layout.activity_friend_list_row, parent, false);
        mViewHolder.friendsname = (TextView) convertView.findViewById(R.id.friendsName); // title
        mViewHolder.thumb_image = (ImageView) convertView.findViewById(R.id.list_image); // thumb image


        mViewHolder.cb = (CheckBox) convertView.findViewById(R.id.checkBox);

        convertView.setTag(mViewHolder);
        mViewHolder.cb.setTag(data.get(position));

        mViewHolder.cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean ischecked) {

                InviteFriends.isChecked[position] = buttonView.isChecked();

            }
        });

    } else {

        mViewHolder = (ViewHolder) convertView.getTag();

    }

    song = mViewHolder.cb.getTag();

    mViewHolder.friendsname.setText(song.get(InviteFriends.KEY_DISPLAY_NAME));
    mViewHolder.imageLoader.DisplayImage(song.get(InviteFriends.KEY_IMAGEPROFILE_URL), thumb_image);
    mViewHolder.cb.setChecked(InviteFriends.isChecked[position]);

    return convertView;
}

答案 1 :(得分:28)

只有在转换视图为空时才应该初始化

这些行

LayoutInflater vi = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

convertView = vi.inflate(R.layout.activity_friend_list_row, parent, false);
// [...] the rest of initialization part
// [...] some changes that must be done at refresh
return convertView;

应如下所示:

if (convertView == null) {
    LayoutInflater vi = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    convertView = vi.inflate(R.layout.activity_friend_list_row, parent, false);
    // [...] the rest of initialization part
}
// [...] some changes that must be done at refresh
return convertView;

目标是回收该列表中现有的视图,而不是在每次滚动列表时显示它时初始化它。

答案 2 :(得分:4)

我的建议是尝试使用convertView = vi.inflate(R.layout.activity_friend_list_row, null); convertView = vi.inflate(R.layout.activity_friend_list_row, parent, false); TextView friendsname = (TextView) convertView.findViewById(R.id.friendsName); // title ImageView thumb_image = (ImageView) convertView.findViewById(R.id.list_image); // thumb image 这可能会对您有帮助。

: - okey ..如果你需要在你的适配器

中使用viewholder类,那么就像这样访问static class ViewHolder { public TextView text; public ImageView image; } @Override public View getView(int position, View convertView, ViewGroup parent) { View rowView = convertView; // reuse views if (rowView == null) { LayoutInflater inflater = context.getLayoutInflater(); rowView = inflater.inflate(R.layout.rowlayout, null); // configure view holder ViewHolder viewHolder = new ViewHolder(); viewHolder.text = (TextView) rowView.findViewById(R.id.TextView01); viewHolder.image = (ImageView) rowView .findViewById(R.id.ImageView01); rowView.setTag(viewHolder); } // fill data ViewHolder holder = (ViewHolder) rowView.getTag(); String s = names[position]; holder.text.setText(s); if (s.startsWith("Windows7") || s.startsWith("iPhone") || s.startsWith("Solaris")) { holder.image.setImageResource(R.drawable.no); } else { holder.image.setImageResource(R.drawable.ok); } return rowView; }

例如

{{1}}

答案 3 :(得分:1)

  

视图适配器的无条件布局膨胀:应该使用View Holder模式(将传递给此方法的回收视图用作第二个参数),以实现平滑滚动。

这意味着您需要在适配器中使用View Holder模式。使用View Holder的目的在于重用视图,因为膨胀和使用findViewById的速度很慢。

使用以下代码时:

public View getView(final int position, View convertView, ViewGroup parent) {

    ViewHolder mViewHolder;
    mViewHolder = new ViewHolder();
    LayoutInflater vi = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    convertView = vi.inflate(R.layout.activity_friend_list_row, parent, false);
    mViewHolder.cb = (CheckBox) convertView.findViewById(R.id.checkBox);
    convertView.setTag(mViewHolder);

    ...

    return convertView;

} 

您不是在重用视图,而是总是创建新视图。

您需要将代码更改为以下内容(请检查注释):

// class for holding the cached view
static class ViewHolder {
   TextView tvFriendsName;
   ImageView imvThumbImage;
   CheckBox cbInviteFriend;
}

public View getView(final int position, View convertView, ViewGroup parent) {

    // holder of the views to be reused.
    ViewHolder viewHolder;

    // get data based on the position
    HashMap<String, String> song = data.get(position);

    // if no previous views found
    if (convertView == null) {
       // create the container ViewHolder
       viewHolder = new ViewHolder();

       // inflate the views from layout for the new row
       LayoutInflater inflater = LayoutInflater.from(parent.getContext());
       convertView = inflater.inflate(R.layout.rowlayout, parent, false);

       // set the view to the ViewHolder.
       viewHolder.cbInviteFriend = convertView.findViewById(R.id.checkBox);
       viewHolder.tvFriendsName  = convertView.findViewById(R.id.friendsName);
       viewHolder.imvThumbImage = convertView.findViewById(R.id.list_image); 

       // save the viewHolder to be reused later.
       convertView.setTag(viewHolder);
    } else {
       // there is already ViewHolder, reuse it.
       viewHolder = (ViewHolder) convertView.getTag();
    }

    // now we can set populate the data via the ViewHolder into views
    viewHolder.tvFriendsName.setText(song.get(InviteFriends.KEY_DISPLAY_NAME));
    imageLoader.DisplayImage(song.get(InviteFriends.KEY_IMAGEPROFILE_URL), viewHolder.imvThumbImage);
    viewHolder.cbInviteFriend.isChecked(InviteFriends.isChecked[position]);

    return convertView;
}

答案 4 :(得分:0)

我所做的是创建一个BaseSpinnerAdapter类,并在需要时重用它

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import androidx.annotation.LayoutRes

/**
 * @param T Model class
 * @param VH ViewHolder class, which holds the view to reuse it
 * */
abstract class BaseSpinnerAdapter<T, VH>(context: Context?, private val items: ArrayList<T>) :
    BaseAdapter() {

    private var inflater: LayoutInflater = LayoutInflater.from(context)

    /** use viewHolder pattern to reusing the views
     * @param p0 current view position
     * @param p1
     * @param viewGroup
     * */
    override fun getView(p0: Int, p1: View?, viewGroup: ViewGroup?): View {
        var pClone = p1
        val viewHolder: VH

        if (pClone == null) {
        pClone = inflater.inflate(setSpinnerItemLayout(), viewGroup, false)
        viewHolder = createViewHolder(pClone)
        pClone?.tag = viewHolder
    } else {
        viewHolder = pClone.tag as VH

    }
        getView(viewHolder, p0)

        return pClone!!
    }


    override fun getItem(p0: Int): T {
        return items[p0]
    }

    override fun getItemId(p0: Int): Long {
        return p0.toLong()
    }

    override fun getCount(): Int {
        return items.size
    }

    @LayoutRes
    abstract fun setSpinnerItemLayout(): Int

    abstract fun getView(viewHolder: VH, position: Int)

    abstract fun createViewHolder(view: View?): VH


}

这里是一个示例,您如何在实现中使用BaseSpinnerAdapter。我相信没有必要详细说明代码说明。

    class SpinnerImplAdapter(context: Context?, private val items: ArrayList<AnyModelClass>) : BaseSpinnerAdapter<AnyModelClass, SpinnerImplAdapter.ViewHolderImp>(context, items) {
    
    
        override fun setSpinnerItemLayout(): Int {
            return R.layout.spinner_item
        }
    
        override fun createViewHolder(view: View?): ViewHolderImp {
            return ViewHolderImp(view)
        }
    
        override fun getView(viewHolder: ViewHolderImp, position: Int) {
            val model = items[position]
            viewHolder.textView?.text = "" // model.etc get anything you want
        }
    
    
/**
 * I have used kotlin extension to get the viewId, 
 * if you are not using then simply call the view.findViewById(R.id.yourView)
 * */
        class ViewHolderImp(view: View?) {
            val textView: TextView? = view?.textView
        }
    
    
    }