有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

使用DPAD快速滚动时,java RecyclerView onCreateViewHolder调用过多

我正在亚马逊Fire TV上开发

因为它是一个电视应用程序(无需触摸),所以我需要在行的布局中设置聚焦,以便能够四处导航

我有一个非常简单的Recyclerview,有图像、文本和一个可聚焦的。当我按下up或down键时,它会正确地滚动和填充,但我注意到,当我的浏览速度超过scroll所能跟上的速度时,它会创建新的视图持有者(屏幕外)并延迟UI

我创建了一个活动,上面有创建编号。当我慢慢滚动时,最高的创造值是10。但当我快速滚动时,我会在一秒钟内得到创建编号为60的卡片。这会导致巨大的延迟,应用程序会丢失很多帧。我的方法完全错了吗

使用下面的代码来测试这一点

/**
 * Created by sylversphere on 15-04-15.
 */
public class LandfillActivity extends Activity{

private Context context;

private static int ticketNumber;
private static int getTicket(){
    ticketNumber ++;
    return ticketNumber;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    context = this;
    setContentView(R.layout.landfill_activity);
    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
    GridLayoutManager glm = new GridLayoutManager(context, 2);
    recyclerView.setLayoutManager(glm);
    SickAdapter sickAdapter = new SickAdapter();
    recyclerView.setAdapter(sickAdapter);
}

public class SickViewHolder extends RecyclerView.ViewHolder{
    TextView ticketDisplayer;
    public ImageView imageView;
    public SickViewHolder(View itemView) {
        super(itemView);
        ticketDisplayer = (TextView) itemView.findViewById(R.id.ticketDisplayer);
        imageView = (ImageView) itemView.findViewById(R.id.imageView);

        itemView.findViewById(R.id.focus_glass).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                context.startActivity(new Intent(context, LouisVuittonActivity.class));
            }
        });
    }
    public void setTicket(int value){
        ticketDisplayer.setText(""+value);
    }
}

public class SickAdapter extends RecyclerView.Adapter<SickViewHolder>{

    @Override
    public SickViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        SickViewHolder svh = new SickViewHolder(getLayoutInflater().inflate(R.layout.one_row_element, null));
        svh.setTicket(getTicket());
        return svh;
    }

    @Override
    public void onBindViewHolder(SickViewHolder holder, int position) {
        String[] image_url_array = getResources().getStringArray(R.array.test_image_urls);
        Picasso.with(context).load(image_url_array[position % image_url_array.length] ).fit().centerCrop().into(holder.imageView);
    }

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

一行元素。xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:安卓="http://schemas.安卓.com/apk/res/安卓"
    安卓:layout_width="match_parent"
    安卓:layout_height="wrap_content"
    安卓:orientation="horizontal">
    <FrameLayout
        安卓:layout_width="match_parent"
        安卓:layout_height="200dp"
        安卓:orientation="horizontal">
        <ImageView
            安卓:id="@+id/imageView"
            安卓:layout_width="match_parent"
            安卓:layout_height="match_parent"
            安卓:adjustViewBounds="true"
            安卓:scaleType="centerCrop"
            安卓:src="@mipmap/sick_view_row_bg" />
        <LinearLayout
            安卓:layout_width="wrap_content"
            安卓:layout_height="wrap_content"
            安卓:layout_gravity="left|center_vertical"
            安卓:layout_marginLeft="15dp"
            安卓:orientation="horizontal">
            <TextView
                安卓:id="@+id/virusTextView"
                安卓:layout_width="wrap_content"
                安卓:layout_height="wrap_content"
                安卓:text="Creation #"
                安卓:textColor="#fff"
                安卓:textSize="40sp" />
            <TextView
                安卓:id="@+id/ticketDisplayer"
                安卓:layout_width="wrap_content"
                安卓:layout_height="wrap_content"
                安卓:text="1"
                安卓:textColor="#fff"
                安卓:textSize="40sp" />
        </LinearLayout>
        <FrameLayout
            安卓:layout_width="match_parent"
            安卓:layout_height="match_parent"
            安卓:id="@+id/focus_glass"
            安卓:background="@drawable/subtle_focus_glass"
            安卓:focusable="true"
            安卓:focusableInTouchMode="true"/>
    </FrameLayout>
</FrameLayout>

测试图片地址。xml(不属于我的URL)

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="test_image_urls"
    formatted="false">
    <item>http://farm4.static.flickr.com/3175/2737866473_7958dc8760.jpg</item>
    <item>http://farm4.static.flickr.com/3276/2875184020_9944005d0d.jpg</item>
    <item>http://farm3.static.flickr.com/2531/4094333885_e8462a8338.jpg</item>
    <item>http://farm4.static.flickr.com/3289/2809605169_8efe2b8f27.jpg</item>
    <item>http://2.bp.blogspot.com/_SrRTF97Kbfo/SUqT9y-qTVI/AAAAAAAABmg/saRXhruwS6M/s400/bARADEI.jpg</item>
    <item>http://fortunaweb.com.ar/wp-content/uploads/2009/10/Caroline-Atkinson-FMI.jpg</item>
    <item>http://farm4.static.flickr.com/3488/4051378654_238ca94313.jpg</item>
    <item>http://farm4.static.flickr.com/3368/3198142470_6eb0be5f32.jpg</item>
    <item>http://www.powercai.net/Photo/UploadPhotos/200503/20050307172201492.jpg</item>
    <item>http://www.web07.cn/uploads/Photo/c101122/12Z3Y54RZ-22027.jpg</item>
    <item>http://www.mitravel.com.tw/html/asia/2011/Palau-4/index_clip_image002_0000.jpg</item>
    <item>http://news.xinhuanet.com/mil/2007-05/19/xinsrc_36205041914150623191153.jpg</item>
    <item>http://ib.berkeley.edu/labs/koehl/images/hannah.jpg</item>
    <item>http://down.tutu001.com/d/file/20110307/ef7937c2b70bfc2da539eea9df_560.jpg</item>
    <item>http://farm3.static.flickr.com/2278/2300491905_5272f77e56.jpg</item>
    <item>http://www.pic35.com/uploads/allimg/100526/1-100526224U1.jpg</item>
    <item>http://img.99118.com/Big2/1024768/20101211/1700013.jpg</item>
    <item>http://farm1.static.flickr.com/45/139488995_bd06578562.jpg</item>
</string-array>
</resources>

微妙的焦点

    <?xml version="1.0" encoding="utf-8"?>
<selector xmlns:安卓="http://schemas.安卓.com/apk/res/安卓">
    <item 安卓:state_focused="true" 安卓:drawable="@color/glass_focus"/>
    <item 安卓:drawable="@color/glass_normal"/>
</selector>

glass_normal#9000

glass_focus#0000


共 (5) 个答案

  1. # 1 楼答案

    通过深入挖掘Sam Judd的答案,我迫使recycler通过在其适配器中实现以下内容来回收视图

    @Override
    public boolean onFailedToRecycleView(@NonNull VH holder) 
          return true;
    }
    

    如你所见here

    Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled due to its transient state. Upon receiving this callback, Adapter can clear the animation(s) that effect the View's transient state and return true so that the View can be recycled. Keep in mind that the View in question is already removed from the RecyclerView.

    In some cases, it is acceptable to recycle a View although it has transient state. Most of the time, this is a case where the transient state will be cleared in onBindViewHolder(ViewHolder, int) call when View is rebound to a new position. For this reason, RecyclerView leaves the decision to the Adapter and uses the return value of this method to decide whether the View should be recycled or not.

    Note that when all animations are created by RecyclerView.ItemAnimator, you should never receive this callback because RecyclerView keeps those Views as children until their animations are complete. This callback is useful when children of the item views create animations which may not be easy to implement using an RecyclerView.ItemAnimator.

    You should never fix this issue by calling holder.itemView.setHasTransientState(false); unless you've previously called holder.itemView.setHasTransientState(true);. Each View.setHasTransientState(true) call must be matched by a View.setHasTransientState(false) call, otherwise, the state of the View may become inconsistent. You should always prefer to end or cancel animations that are triggering the transient state instead of handling it manually.

  2. # 2 楼答案

    尝试增加池中的maximum number of recycled views

    recyclerView.getRecycledViewPool().setMaxRecycledViews(50);
    

    50是一个任意数字,你可以尝试更高或更低,看看会发生什么

    RecyclerView试图避免重复使用具有transient state的视图,因此,如果视图很快失效或正在制作动画,它们可能不会立即重复使用

    类似地,如果有许多较小的视图,屏幕上显示的内容可能会超过默认池大小所能处理的内容(在类似网格的布局中更常见)

  3. # 3 楼答案

    对于其他想要快速破解的人, 做这个。这将延迟选择,直到充气并选择为止。 我不知道它是怎么工作的。 只需在onFocusSearchFailed上返回null

     /**
     * Created by sylversphere on 15-04-22.
     */
    public class SomeGridLayoutManager extends GridLayoutManager{
    
        private final Context context;
    
        public SomeGridLayoutManager(Context context, int spanCount) {
            super(context, spanCount);
            this.context = context;
        }
    
        public SomeGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
            super(context, spanCount, orientation, reverseLayout);
            this.context = context;
        }
    
        @Override
        public View onFocusSearchFailed(View focused, int focusDirection, RecyclerView.Recycler recycler, RecyclerView.State state) {
            return null;
        }
    }
    
  4. # 4 楼答案

    正如评论者指出的,毕加索的悬而未决的回应可能会阻碍你。如果是这种情况,可以通过扩展ImageView并重写以下方法来解决。我觉得值得一试

    @Override
    protected void onDetachedFromWindow() {
        Picasso.with(context).cancelRequest(this);
        super.onDetachedFromWindow();
    }
    

    更新:

    事实证明,这不是正确的方式,任何想要取消请求的人都应该在onViewRecycled()回调中这样做,正如下面的评论所指出的

  5. # 5 楼答案

    毕加索在阻碍你,但建议的建立自己机制的解决方案不是可行的

    毕加索在过去一年左右的时间里落在了后面,如今有了更好的替代品,形式为Google GlideFacebook Fresco,它们专门发布了更新,以更好地与RecyclerView配合使用,并在许多在线测试中被证明在加载、缓存和存储方面更快、更高效,例如:

    我希望这有帮助。 祝你好运