一.問題簡述:
ViewPager一般常用於與Fragment結合或者與View結合使用,當佈局只有ViewpPager時,View的加載十分的完美,當加入RecycleView時出現了問題,第一張和第二張View可以加載,但是第三張以後是空白View,這不是重點,重點是調試的時候,ViewPager的View的加載又是十分的完美,而運行的時候又會出現加載空白問題,真的是感覺醉醉的。。。
二.問題分析:
經過我的調試排除了以下可能(可能出現空白的原因):
①mImageView的問題:序號問題、越界問題、mImageView沒獲得數據問題(反正就是非常的確定不是mImageView的問題)
②adpater的問題:頁面被覆蓋問題:加載和釋放圖片時間不合理問題(反正就是非常的確定不是adapter的問題)
畢竟沒加入RecycleView時一切正常,所以排除了上述可能原因。
1.Fragment的onCreateView()代碼,模塊ViewPager的adapter代碼模塊:
public class HomePageFragment extends Fragment { ... @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = LayoutInflater.from(getContext()).inflate(R.layout.home_page, container, false); ButterKnife.bind(this, view); init(); //ViewPager模塊 pointInit(); mMyPagerAdapter = new MyPagerAdapter(mImageViews); mImagePager.setAdapter(mMyPagerAdapter); mMyThread.start(); //這是開啓線程每隔一段時間切花圖片線程(不用關注) viewPagerChange(); //ViewPager換頁監聽器(不用關注) //RecycleView列表 initRecycleViewList(); //獲取到RecycleView列表的數據(不用關注) mMyRecycleViewAdapter = new MyRecycleViewAdapter(getContext(),mLists); mRecycleView.setAdapter(mMyRecycleViewAdapter); LinearLayoutManager mLinearLayoutManager = new LinearLayoutManager(getContext(),LinearLayoutManager.VERTICAL,false); mRecycleView.setLayoutManager(mLinearLayoutManager); return view; } class MyPagerAdapter extends PagerAdapter{ private List<ImageView> mImageViews; public MyPagerAdapter(List<ImageView> mImageViews) { this.mImageViews = mImageViews; } @Override public int getCount() { return mImageViews.size(); } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Object instantiateItem(ViewGroup container, int position) { mImageViews.get(position).setScaleType(ImageView.ScaleType.FIT_XY); container.addView(mImageViews.get(position)); return mImageViews.get(position); } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView(mImageViews.get(position)); } } //其他代碼... }
2.recycleView的adapter中的ViewHolder的綁定的代碼。
@Override public void onBindViewHolder(final MyViewHolder holder, final int position) { HttpUtil.sendHttpRequest(mList.get(position).getmTourismImageString(), new HttpCallbackListener() { @Override public void onFinish(byte[] picByte) { Bitmap bitmap = BitmapFactory.decodeByteArray(picByte, 0, picByte.length); bitmapList.add(bitmap); holder.mTourismImage.setImageBitmap(bitmap); } @Override public void onError(Exception e) { e.printStackTrace(); } }); holder.mTourismName.setText(mList.get(position).getmTourismNameString()); holder.mTravelDistance.setText(mList.get(position).getmTravelDistance()); holder.mDescribe.setText(mList.get(position).getmDescribe()); holder.mPrice.setText(mList.get(position).getmPrice()+""); holder.mRatingBar.setRating((float) mList.get(position).getmRatingBarProgress()); holder.mTicketsSold.setText(mList.get(position).getmTicketsSoldNum()+""); }
這是recycleView的adapter方法中的綁定ViewHolder方法的代碼,大體是通過接口回調機制獲取到網絡圖片的byte數組,再轉化爲bitmap從而給mTourismImage賦值。
大體說一說接口回調機制:主要是爲了解決某些數據獲取需要時間,當執行到此步時,使用接口回調機制當獲取到數據是再去執行,從而解決未獲取到數據時進行該操作。
3.回調接口HttpCallbackListener 與獲取網絡圖片Http類(自定義的)的代碼:
①回調接口HttpCallbackListener
public interface HttpCallbackListener { void onFinish(byte[] picByte); //獲取到數據時執行該方法 void onError(Exception e); //未獲取到數據時執行該方法 }②獲取網絡圖片Http類
public class HttpUtil { public static byte[] picByte; public static void sendHttpRequest(final String address,final HttpCallbackListener listener){ new Thread(new Runnable() { @Override public void run() { try { Log.d("TAG","請求運行了"); URL url = new URL(address); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setReadTimeout(10000); if(connection.getResponseCode() == 200) { InputStream is = connection.getInputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] bytes = new byte[1024]; int length = -1; while((length = is.read(bytes)) != -1) { bos.write(bytes,0,length); } picByte = bos.toByteArray(); is.close(); bos.close(); if(listener != null) { listener.onFinish(picByte); } } } catch (Exception e) { if(listener != null) { listener.onError(e); } e.printStackTrace(); } } }).start(); } }運行時,網絡圖片的卻加載出來了,然而viewPager加載View時第三張以後就出現了空白,本人當時感覺recyleView的實現與ViewPager的實現並沒有影響呀,爲什麼會這樣???
最後我發現ViewPager與RecycleView的關聯只有一個,那就是他們合起來生成了Fragment(注意這裏的Fragment所適配的ViewPager是另外一個,不需要與文中的ViewPager聯繫),給Fragment返回了一個View,也就是說網絡圖片生成時影響了這個View從而造成了空白的產生。
三.問題解決:
①原因分析:通過仔細的觀察網絡圖片的加載過程發現,加載圖片是在接口回調機制中實現的,而該接口的方法實現是在Http中開啓了一個子線程實現的,說到這裏就有人明白了,弄了半天原來是因爲接口回調機制中存在子線程,當我們使用接口回調機制時不經意的在子線程中對UI進行了操作(自己以爲是主線程!!),其實這點還真不容易發現,畢竟以接口做參數的Http.sendHttpRequest(final String address,final HttpCallbackListener listener)方法是在主線程中實現的,看起來就像是在主線程中對UI進行的操作。
② 解決方法:通過Handler發送信息,在Handler中對UI進行操作。源碼如下:
public class MyRecycleViewAdapter extends RecyclerView.Adapter<MyViewHolder> { private MyViewHolder mMyViewHolder; private LayoutInflater mLayoutInflater; private Context mContext; private List<TouristInformation> mList; public static List<ImageView> mImageViewList; public static List<Bitmap> bitmapList; private MyHandler myHandler; public MyRecycleViewAdapter(Context context, List<TouristInformation> mList) { this.mContext = context; this.mList = mList; bitmapList = new ArrayList<>(); mImageViewList = new ArrayList<>(); myHandler = new MyHandler(); mLayoutInflater = LayoutInflater.from(context); } @Override public MyViewHolder onCreateViewHolder(ViewGroup ag0, int viewType) { View view = mLayoutInflater.inflate(R.layout.tourist_information, ag0, false); mMyViewHolder = new MyViewHolder(view); return mMyViewHolder; } @Override public void onBindViewHolder(final MyViewHolder holder, final int position) { HttpUtil.sendHttpRequest(mList.get(position).getmTourismImageString(), new HttpCallbackListener() { @Override public void onFinish(byte[] picByte) { Bitmap bitmap = BitmapFactory.decodeByteArray(picByte, 0, picByte.length); bitmapList.add(bitmap); myHandler.sendEmptyMessage(0x123); } @Override public void onError(Exception e) { e.printStackTrace(); } }); mImageViewList.add(holder.mTourismImage); holder.mTourismName.setText(mList.get(position).getmTourismNameString()); holder.mTravelDistance.setText(mList.get(position).getmTravelDistance()); holder.mDescribe.setText(mList.get(position).getmDescribe()); holder.mPrice.setText(mList.get(position).getmPrice()+""); holder.mRatingBar.setRating((float) mList.get(position).getmRatingBarProgress()); holder.mTicketsSold.setText(mList.get(position).getmTicketsSoldNum()+""); } @Override public int getItemCount() { return mList.size(); } class MyHandler extends Handler{ @Override public void handleMessage(Message msg) { if(msg.what == 0x123) { mImageViewList.get(0).setImageBitmap(bitmapList.get(0));//我這裏只給第一個設置了bitmap } } } } class MyViewHolder extends RecyclerView.ViewHolder { @BindView(R.id.tourismImage) ImageView mTourismImage; @BindView(R.id.touristName) TextView mTourismName; @BindView(R.id.travelDistance) TextView mTravelDistance; @BindView(R.id.describe) TextView mDescribe; @BindView(R.id.price) TextView mPrice; @BindView(R.id.ratingBar) RatingBar mRatingBar; @BindView(R.id.ticketsSold) TextView mTicketsSold; public MyViewHolder(View view) { super(view); ButterKnife.bind(this, view); } }③總結:使用接口回調機制時最好通過Handler對UI進行操作,防止出現莫名其妙的bug。