我正在开发一个应用程序,其中有一个项目列表,每个项目都包含一个viewpager,在viewpager下面你有其他小部件(文本,按钮......等)。我必须从服务器加载图像的url并在我的viewpager中显示图像
这是我的Viewpager适配器:
android:windowSoftInputMode
}
以下是我的回收商视图项目的一部分:
public class ImageAdapter extends PagerAdapter {
private Context mContext;
private ArrayList<String> imagePaths;
private static final String TAG = ImageAdapter.class.getName();
// constructor
public FullScreenImageAdapter(Context context, ArrayList<String> imagePaths){
this.mContext = context;
this.imagePaths = imagePaths;
}
@Override
public int getCount() {
return null == imagePaths ? 0 : this.imagePaths.size();
}
@Override
public Object instantiateItem(final ViewGroup container, final int position) {
final LayoutInflater inflater = LayoutInflater.from(mContext);
final View viewLayout = inflater.inflate(R.layout.full_screen_image, container, false);
final ImageView image = (ImageView) viewLayout.findViewById(R.id.image);
final ProgressBar progressBar = (ProgressBar)viewLayout.findViewById(R.id.pBar);
String url = BuildConfig.URL_API_IMAGES;
final SparseArray<Object> mBitmapMap = new SparseArray<>();
Glide.with(mContext)
.load(url + imagePaths.get(position)).asBitmap()
.placeholder(R.drawable.car)
.dontAnimate()
.into(new BitmapImageViewTarget(image){
@Override
public void onLoadStarted(Drawable placeholder){
super.onLoadStarted(placeholder);
image.setImageDrawable(placeholder);
}
@Override
public void onResourceReady(Bitmap resource,
GlideAnimation<? super Bitmap> glideAnimation){
super.onResourceReady(resource, glideAnimation);
mBitmapMap.put(position, resource);
progressBar.setVisibility(View.INVISIBLE);
image.setImageBitmap(resource);
}
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable){
super.onLoadFailed(e, errorDrawable);
progressBar.setVisibility(View.INVISIBLE);
}
});
container.addView(viewLayout);
return viewLayout;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
try{
container.removeView((RelativeLayout) object);
Glide.clear(((View) object).findViewById(R.id.image));
unbindDrawables((View) object);
object = null;
}catch (Exception e) {
Log.w(TAG, "destroyItem: failed to destroy item and clear it's used resources", e);
}
}
protected void unbindDrawables(View view) {
if (view.getBackground() != null) {
view.getBackground().setCallback(null);
}
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
}
}
@Override
public boolean isViewFromObject(View view, Object object){
return view == ((RelativeLayout) object);
}
这是我在Recyclerview适配器
中的(部分)视图类<RelativeLayout
android:id="@+id/rl_pictureLayout"
android:layout_width="match_parent"
android:layout_marginLeft="@dimen/dimen_5"
android:layout_marginRight="@dimen/dimen_5"
android:layout_marginTop="@dimen/dimen_5"
android:layout_height="@dimen/sliderLayoutHeight">
<ViewPager
android:id="@+id/vp_photos"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<me.relex.circleindicator.CircleIndicator
android:id="@+id/indicator"
android:layout_width="match_parent"
android:layout_marginBottom="50dp"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
/>
<ImageButton
android:id="@+id/ib_bookmark"
android:background="@color/transparent"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="@dimen/dimen_16"
android:layout_marginRight="@dimen/dimen_16"
android:layout_marginEnd="@dimen/dimen_16"
android:src="@drawable/ic_bookmark_border_white_24dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/tv_picture_counter"
android:textColor="@color/white"
android:textAllCaps="true"
android:fontFamily="sans-serif"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginLeft="@dimen/dimen_16"
android:layout_marginStart="@dimen/dimen_16"
android:layout_marginBottom="@dimen/dimen_24"
/>
<TextView
android:id="@+id/tv_damagedPhotos"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:fontFamily="sans-serif"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginEnd="@dimen/dimen_16"
android:layout_marginRight="@dimen/dimen_16"
android:layout_marginBottom="@dimen/dimen_24"
android:background="@color/transparent"
android:drawableRight="@drawable/ic_photo_camera_red_24dp"
android:drawableEnd="@drawable/ic_photo_camera_red_24dp"
android:drawablePadding="@dimen/dimen_5"
/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_timerLayout"
android:layout_below="@id/rl_pictureLayout"
android:layout_marginTop="@dimen/dimen_5"
android:layout_alignLeft="@id/rl_pictureLayout"
android:layout_alignStart="@id/rl_pictureLayout"
android:layout_alignRight="@id/rl_pictureLayout"
android:layout_alignEnd="@id/rl_pictureLayout"
android:layout_marginBottom="@dimen/dimen_10"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_timer"
style="@style/text_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:textColor="@color/ColorBlack"
android:textStyle="bold"
/>
<TextView
android:id="@+id/tv_highest_bid"
style="@style/text_item"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
/>
<Button
android:id="@+id/btn_bid"
style="@style/button"
android:layout_width="60dp"
android:layout_height="30dp"
android:textAllCaps="false"
android:background="@drawable/curved_border_bgd_red"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:stateListAnimator="@null"
android:text="@string/btn_text_bid"
/>
</RelativeLayout>
在onBindViewHolder中,这就是我所拥有的(就viewpager而言):
static class ViewHolder extends RecyclerView.ViewHolder
{
@BindView(R.id.indicator)
CircularIndicator mCircleIndicator;
@BindView(R.id.vp_photos)
ViewPager mViewPagerPhotoView;
public ViewHolder(View itemView)
{
super(itemView);
ButterKnife.bind(this, itemView);
}
}
现在我的reclerview有10个项目,我从服务器加载。事情是,我注意到,当我向下滚动说到列表中的第7项时,一切似乎都很好但是当我向上滚动以查看我的回收列表的第一项时,在第一项的viewpager中找到的图像倾向于消失并被(暂时)替换为占位符图像,即viewpager完全清空,我暂时占有占位符;如果该项目在我的屏幕上仍然可见,则viewpager a的图像将再次从服务器加载。我选择了Glide,因为它的缓存功能众所周知。此外,viewpager最多可以有20个图像 !! ...
有没有人知道如何解决这个问题? 我做错了什么? 任何帮助将不胜感激.....
答案 0 :(得分:2)
&#34;问题&#34;是Recyclerview的项目将被回收。您永远不会保存加载的图像。如果向下和向上滚动,将调用onBindViewHolder
并创建一个新的适配器实例,这样每次调用onBindViewHolder
时都会反复加载图像。
解决方案是保存适配器对象(在map - &gt;位置,适配器中)并将其绑定到onBindViewHolder
内。
ImageAdapter adapter = adaptermap.get(postion);
int adapterPosition = 0;
if(adapter == null) {
adapter = new ImageAdapter(mContext, item.getUrlPhotos());
adaptermap.put(position, adapter);
adapterPosition = viewPageStates.get(position);
}
holder.mViewPagerPhotoView.setAdapter(adapter);
holder.mViewPagerPhotoView.setCurrentItem(adapterPosition);
您还必须保存viewpager的位置。
将此作为成员:
// you also can use a SparseIntArray for better performance
// if the itemcount is less than a few hundret
public HashMap<Integer, Integer> viewPageStates = new HashMap<>();
还要在您的recyclerview适配器中添加:
@Override
public void onViewRecycled(YourViewHolderClass holder) {
int position = holder.getAdapterPosition();
viewPageStates.put(position, holder.mViewPagerPhotoView.getCurrentItem());
super.onViewRecycled(holder);
}
重要:强>
我的解决方案只有在您不添加/删除/排序项目时才有效,因为我们保存了适配器位置。如果你还需要它,你必须修改添加/删除/排序等地图
答案 1 :(得分:1)
尝试在ViewHolder中保留ImageAdapter的实例:
static class ViewHolder extends RecyclerView.ViewHolder
{
@BindView(R.id.indicator)
CircularIndicator mCircleIndicator;
@BindView(R.id.vp_photos)
ViewPager mViewPagerPhotoView;
ImageAdapter mAdapter;
public ViewHolder(View itemView)
{
super(itemView);
ButterKnife.bind(this, itemView);
mAdapter = new ImageAdapter();
mViewPagerPhotoView.setAdapter(mAdapter);
}
//Add bind() method which will be called in onBindViewHolder()
public void bind(ArrayList<String> imagePaths) {
//... maybe some other bindings
mAdapter.init(imagePaths);
mViewPagerPhotoView.setOffscreenPageLimit(mAdapter.getCount() - 1);
mCircleIndicator.setViewPager(mViewPagerPhotoView);
}
}
在适配器中创建init(ArrayList<String> imagePaths)
方法:
void init(ArrayList<String> paths) {
this.imagePaths = paths;
notifyDataSetChanged();
}
应该有所帮助。如果没有帮助,那么你使用Glide会出现问题。
答案 2 :(得分:1)
问题在于图像。你说“一个viewpager最多可以有20个图像”,我不知道你的意思是什么,最多20个.ViewPager设计为无限项,除非你把所有项目加载到一个没有意义的项目。
正如你所描述的那样,我想问题是你的图像正在回收,为什么当你向上滚动时你不会再看到你的图像,直到Glide加载图像并再次渲染它。
所以解决方案并不是很简单,但首先让我解释一下image和viewpager。假设您在recyclerview适配器中有10个项目,并且recyclerview根据需要仅加载了5个项目。所以现在需要在内存中的最小总图像是5 x 2(每个viewpager需要至少2个图像)总共等于10个图像。这是巨大的,它取决于你的形象,如果你的图像非常大,你将耗尽内存,当Glide将破坏一些图像,为新的来临腾出空间。希望你能看到正在加载图像的影响。
现在我的建议是:
但是,您可以使用RecyclerView.ViewCacheExtension来管理您自己的视图回收器,但我认为它不会有所帮助。此外,您还可以为Pager适配器实现缓存视图,ViewPager deos不回收任何视图,因此当ViewPager需要View时,它只需调用instantiateItem并创建View。