Android練手小項(xiàng)目(KTReader)基于mvp架構(gòu)(五)

上路傳送眼:

Android練手小項(xiàng)目(KTReader)基于mvp架構(gòu)(四)

GIthub地址: https://github.com/yiuhet/KTReader

上篇文章中我們完成了豆瓣模塊。
而這次我們要做的的就是圖片模塊。

效果圖奉上:


效果圖.gif

總覽一下圖片模塊我們完成的功能有:

  • 圖片瀏覽(三種排序方式)
  • 圖片關(guān)鍵字搜索
  • 圖片詳情查看
  • 圖片下載(可查看和選擇圖片質(zhì)量)
  • 下拉刷新,上拉加載

所用到的一些知識(shí)點(diǎn)有:

  • Rxjava2 + Retrofit2 + OKhttp3 實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求
  • 下載文件的FileOutputStream用法
  • 下載文件的大小獲取回調(diào)
  • AlertDialog的適配器視圖和Items視圖
  • Spinner的簡(jiǎn)單使用
  • RecycleView的瀑布流布局實(shí)現(xiàn)和踩坑(item重排)
  • SwipeRefreshLayout布局

可完善和加強(qiáng)的內(nèi)容或功能有:

  • 收藏圖片
  • 圖片詳情頁的美化
  • 下載列表的實(shí)現(xiàn)
  • 下載圖片時(shí)通知欄的進(jìn)度提醒
  • 隨意推薦一張圖片

大體上介紹完畢之后,下面就說說具體的實(shí)現(xiàn):

1. API的獲取

想要做一個(gè)圖片模塊,數(shù)據(jù)是必不可少的,這里我們直接使用免費(fèi)的api就行了,話說我百度出來的圖片api為啥都是美女圖啊喂,雖然很福利!為嘛找個(gè)綜合圖片的api那么麻煩啊,總之找了好久終于找到了個(gè)好用的api,分享給大家:Unsplash(唯一的不足就是沒有中文的開發(fā)者文檔,不過其實(shí)里面的英語挺好懂的,實(shí)在不行還可以劃詞翻譯嘛)。
另外附上我找資源時(shí)發(fā)現(xiàn)的一個(gè)好東西,以后找api時(shí)可以直接先在這搜:

總之,注冊(cè)好開發(fā)者賬號(hào)和看了開發(fā)者文檔后就可以直接寫接口了。

api.TupianApi

public interface TupianApi {
    /*
     *Get a single page from the list of all photos.
     * 參數(shù)分別為appId,頁碼,每頁個(gè)數(shù),排序規(guī)則 (Valid values: latest, oldest, popular; default: latest)
     */
    @GET("photos")
    Observable<List<UnsplashPhotosList>> getUnsplashPhotosList
        (@Query("client_id") String clientId,@Query("page") int page,@Query("per_page") int perPage,
         @Query("order_by") String orderBy);

    @GET("/search/photos")
    Observable<UnsplashPhotoSearch> getSearchUnsplashPhotosList
            (@Query("client_id") String clientId,@Query("query") String query,@Query("page") int page,
             @Query("per_page") int perPage);

    @GET("photos/random")
    Observable<UnsplashPhoto> getUnsplashRandomPhoto(@Query("client_id") String clientId);

    @GET("photos/{id}")
    Observable<UnsplashPhoto> getUnsplashPhoto
        (@Path("id")  String id, @Query("client_id") String clientId);

    @GET("photos/{id}/download")
    Observable<UnsplashUrl> getPhotoDownloadUrl(
            @Path("id")  String id, @Query("client_id") String clientId);

    @GET
    Observable<ResponseBody> getPhotoDownload(@Url String url);

}

然后在RetrofitManager類里添加個(gè)獲取圖片服務(wù)的方法
utils.RetrofitManager

private TupianApi tupianApi;
public TupianApi getTupianService(String url) {
        if (tupianApi == null) {
            tupianApi = new Retrofit.Builder()
                    .baseUrl(url) //必須以‘/’結(jié)尾
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//使用RxJava2作為CallAdapter
                    .client(client)//如果沒有添加,那么retrofit2會(huì)自動(dòng)給我們添加了一個(gè)。
                    .addConverterFactory(GsonConverterFactory.create())//Retrofit2可以幫我們自動(dòng)解析返回?cái)?shù)據(jù),
                    .build().create(TupianApi.class);
        }
        return tupianApi;
    }

2. 實(shí)現(xiàn)M層

實(shí)體類老規(guī)矩直接GsonFormat生成,不過Unsplash返回的直接是數(shù)組類型,以至于工具生成的實(shí)體類為數(shù)組里的具體項(xiàng),因此我們?cè)趯慳pi接口時(shí)類型要為L(zhǎng)ist類型,這樣才能解析出列表數(shù)據(jù)。

  • M層的接口
    我們需要的功能為加載查詢的關(guān)鍵字圖片數(shù)據(jù)和默認(rèn)加載數(shù)據(jù)
    model.UnsplashPhotoListModel
public interface UnsplashPhotoListModel {
    void loadSearchPhotoList(OnUnsplashPhotoListListener listener,String query);
    void loadPhotoList(OnUnsplashPhotoListListener listener);
}
  • M層的實(shí)現(xiàn)類
    實(shí)現(xiàn)類里我們定義了挺多變量,是為了控制獲取的數(shù)據(jù)數(shù)量和類型。
    分別有頁碼,每頁圖片數(shù)量,和排序方式等。
    model.UnsplashPhotoListModelImp1
public class UnsplashPhotoListModelImp1 implements UnsplashPhotoListModel{

    private static final String UNSPLASH_APPLICATION_ID = "nideapplicationidya233";
    private static final String UNSPLASH_BASE_URL = "https://api.unsplash.com/";
    private int PAGE = 1;
    private int PAGE_SEARCH = 1;
    private int PER_PAGE = 10;
    private String[] ORDERBY = {"latest", "oldest", "popular"};
    //排序方式 ,0,1,2分別為"latest", "oldest", "popular"。
    private int STATE = 0;
    private TupianApi mTupianApi;

    public UnsplashPhotoListModelImp1() {
        mTupianApi = RetrofitManager
                .getInstence()
                .getTupianService(UNSPLASH_BASE_URL);
    }

    public void setState(int state) {
        STATE = state;
    }

    @Override
    public void loadSearchPhotoList(final OnUnsplashPhotoListListener listener, String query) {
        if (mTupianApi != null) {
            mTupianApi.getSearchUnsplashPhotosList(UNSPLASH_APPLICATION_ID, query, PAGE_SEARCH, PER_PAGE)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Observer<UnsplashPhotoSearch>() {
                        @Override
                        public void onSubscribe(@NonNull Disposable d) {
                        }

                        @Override
                        public void onNext(@NonNull UnsplashPhotoSearch unsplashPhotosLists) {
                            Log.d("testquery","onNext");
                            listener.onLoadSearchPhotoListSuccess(unsplashPhotosLists);
                            PAGE_SEARCH ++;
                        }

                        @Override
                        public void onError(@NonNull Throwable e) {
                            Log.d("testquery",e.toString());
                            listener.onLoadDataError(e.toString());
                        }

                        @Override
                        public void onComplete() {
                        }
                    });
        }
    }

    @Override
    public void loadPhotoList(final OnUnsplashPhotoListListener listener) {
        if (mTupianApi != null) {
            mTupianApi.getUnsplashPhotosList(UNSPLASH_APPLICATION_ID, PAGE, PER_PAGE, ORDERBY[STATE])
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Observer<List<UnsplashPhotosList>>() {
                        @Override
                        public void onSubscribe(@NonNull Disposable d) {
                        }

                        @Override
                        public void onNext(@NonNull List<UnsplashPhotosList> unsplashPhotosLists) {
                            listener.onLoadPhotoListSuccess(unsplashPhotosLists);
                            PAGE++;
                        }

                        @Override
                        public void onError(@NonNull Throwable e) {
                            listener.onLoadDataError(e.toString());
                        }

                        @Override
                        public void onComplete() {
                        }
                    });
        }
    }
}

3. 實(shí)現(xiàn)V層

首先確定我們要實(shí)現(xiàn)的功能,大致分為4種:

  • 開始獲取數(shù)據(jù)
  • 獲取默認(rèn)數(shù)據(jù)成功
  • 獲取查詢數(shù)據(jù)成功
  • 獲取數(shù)據(jù)失敗

詳情代碼如下
view.UnsplashPhotoListView

public interface UnsplashPhotoListView {
    void onStartGetData();

    void onGetPhotoSuccess(List<UnsplashPhotosList> photosList);

    void onGetSearchPhotoSuccess(UnsplashPhotoSearch photosList);

    void onGetDataFailed(String error);
}

然后可以寫具體的界面
這里我們同樣分析一下界面需要如何實(shí)現(xiàn):
首先我們需要個(gè)搜索欄和選擇欄,然后再來個(gè)recycleview來加載圖片,為了美觀我們采用瀑布流的布局,當(dāng)內(nèi)容拉到底部時(shí),我們要加載更多的圖片,這是我們就需要個(gè)變量標(biāo)識(shí)我們需要加載更多的是用戶自己搜索的圖片還是默認(rèn)加載的圖片(哪種排序方式),同時(shí)這個(gè)標(biāo)識(shí)用于傳遞給P層通知它要加載的數(shù)據(jù)類型。

分析完成后就是具體代碼的實(shí)現(xiàn):
ui.fragment.UnsplashListFragment

public class UnsplashListFragment extends BaseFragment<UnsplashPhotoListView, UnsplashPhotoListPresenterImp1> implements UnsplashPhotoListView, SwipeRefreshLayout.OnRefreshListener {

    private UnsplashListAdapter unsplashListAdapter;
    private int STATE = 0;//當(dāng)前的狀態(tài)
    //擁有的狀態(tài) :latest", "oldest", "popular",搜索狀態(tài)。
    private final int STATE_LATEST = 0;
    private final int STATE_OLDEST = 1;
    private final int STATE_POPULAR = 2;
    private final int STATE_SEARCH = 3;
    //記錄搜索狀態(tài)下的搜索項(xiàng)
    private String Query;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = super.onCreateView(inflater, container, savedInstanceState);
        unbinder = ButterKnife.bind(this, rootView);
        init();
        return rootView;
    }

    private void init() {
        initRecycleView();
        //監(jiān)聽 SwipeRefreshLayout 事件 上拉就調(diào)用ReLoad方法。
        mContentSwipe.setOnRefreshListener(this);
        //監(jiān)聽 SearchView 事件
        mSvPhoto.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                Query = query;
                if (query != null){
                    STATE = STATE_SEARCH;
                } else {
                    STATE = STATE_LATEST;
                }
                ReLoad(query);
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                return false;
            }
        });
        //初始化加載數(shù)據(jù) Spinner 開始選擇latest
        mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                STATE = position;
                ReLoad("");
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {

            }
        });
    }

    private void initRecycleView() {
        contentRecycle.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
        contentRecycle.setHasFixedSize(true);
        contentRecycle.setItemAnimator(new DefaultItemAnimator());
        unsplashListAdapter = new UnsplashListAdapter(getContext());
        unsplashListAdapter.setOnItemClickListener(mOnItemClickListener);
        SpacesItemDecoration decoration = new SpacesItemDecoration(12);
        contentRecycle.addItemDecoration(decoration);
        contentRecycle.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (isSlideToBottom(recyclerView)) {
                    if (STATE == STATE_SEARCH) {
                        mPresenter.getSearchPhotoList(Query);
                    } else {
                        mPresenter.getPhotoList(STATE);
                    }
                }
            }
        });
        contentRecycle.setAdapter(unsplashListAdapter);
    }

    //判斷RecycleView是否到底部
    public static boolean isSlideToBottom(RecyclerView recyclerView) {
        if (recyclerView.computeVerticalScrollExtent() + recyclerView.computeVerticalScrollOffset()
                >= recyclerView.computeVerticalScrollRange()){
            return true;
        }
        return false;
    }

    //RecycleView的item監(jiān)聽事件 點(diǎn)擊item,跳轉(zhuǎn)到相應(yīng)的activity
    private UnsplashListAdapter.OnItemClickListener mOnItemClickListener = new UnsplashListAdapter.OnItemClickListener() {
        @Override
        public void onItemClick(String id,View view) {
            Intent intent = new Intent(getActivity(), UnsplashPhotoActivity.class);
            intent.putExtra("PHOTOID", id);
            CircularAnimUtil.startActivity(getActivity(), intent, view,
                    R.color.colorPrimary);
            toast("you touch me,my id:" + id);
        }
    };

    //重新加載數(shù)據(jù) ,首先清空數(shù)據(jù),然后根據(jù)當(dāng)前狀態(tài)獲取數(shù)據(jù)
    public void ReLoad(String query) {
        unsplashListAdapter.clearData();
        if (STATE == STATE_SEARCH) {
            mPresenter.getSearchPhotoList(query);
        } else {
            mPresenter.getPhotoList(STATE);
        }
    }

    @Override
    public void onStartGetData() {
        if (mContentSwipe != null) {
            mContentSwipe.setRefreshing(true);
        }
    }

    @Override
    public void onGetPhotoSuccess(List<UnsplashPhotosList> photosList) {
        if (mContentSwipe != null && mContentSwipe.isRefreshing()) {
            mContentSwipe.setRefreshing(false);
        }
        unsplashListAdapter.addData(photosList);
    }

    @Override
    public void onGetSearchPhotoSuccess(UnsplashPhotoSearch photosList) {
        if (mContentSwipe != null && mContentSwipe.isRefreshing()) {
            mContentSwipe.setRefreshing(false);
        }
        unsplashListAdapter.addData(photosList.results);
    }

    @Override
    public void onGetDataFailed(String error) {
        if (mContentSwipe != null && mContentSwipe.isRefreshing()) {
            mContentSwipe.setRefreshing(false);
        }
        toast(error);
    }

    @Override
    protected int getLayoutRes() {
        return R.layout.fragment_unsplash;
    }

    @Override
    protected UnsplashPhotoListPresenterImp1 createPresenter() {
        return new UnsplashPhotoListPresenterImp1(this);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        unbinder.unbind();
    }

    @Override
    public void onRefresh() {
        ReLoad("");
    }
}

這里有個(gè)坑,當(dāng)RecycleView設(shè)置為StaggeredGridLayoutManager布局方式時(shí),我們會(huì)發(fā)現(xiàn)RecycleView的item項(xiàng)會(huì)變位置,原來是每次其高度都隨機(jī)導(dǎo)致,這里我們?cè)赼dapter內(nèi)部維護(hù)一個(gè)高度數(shù)組,讓內(nèi)部的item有著不變的隨機(jī)高度。

其具體實(shí)現(xiàn)代碼我也貼出 如下:
adapter.UnsplashListAdapter

public class UnsplashListAdapter extends RecyclerView.Adapter<UnsplashListAdapter.PhotoViewHolder>{

    private List<UnsplashPhotosList> mPhotoList;
    private Context mContext;
    private OnItemClickListener mItemClickListener;
    //用于每個(gè)item的布局
    private LayoutInflater mInflater;
    //瀑布流控制高度
    private List<Integer> mHeights;
    private int currentPos = 0;

    public UnsplashListAdapter(Context context) {
        mContext = context;
        mPhotoList = new ArrayList<>();
        mInflater = LayoutInflater.from(context);
        mHeights = new ArrayList<Integer>();
    }


    public void addData(List<UnsplashPhotosList> unsplashPhotosList) {
        currentPos += mPhotoList.size();
        for (UnsplashPhotosList photosList:unsplashPhotosList){
            mPhotoList.add(photosList);
        }
        for (int i = 0;i<unsplashPhotosList.size();i++){
            mHeights.add((int) (300+Math.random()*300));
        }
        //應(yīng)該局部 刷新 ,有個(gè)bug TODO: 2017/6/11  bug
        //notifyItemRangeChanged(currentPos,unsplashPhotosList.size());
        notifyDataSetChanged();
    }

    //清空數(shù)據(jù)
    public void clearData() {
        currentPos = 0;
        mPhotoList.clear();
        notifyDataSetChanged();
    }


    @Override
    public PhotoViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout.item_photo, parent, false);
        PhotoViewHolder photoViewHolder = new PhotoViewHolder(view);
        return photoViewHolder;
    }

    @Override
    public void onBindViewHolder(final PhotoViewHolder holder, final int position) {

        ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
        layoutParams.height = mHeights.get(position);
        holder.itemView.setLayoutParams(layoutParams);
        Glide.with(mContext)
                .load(mPhotoList.get(position).urls.small)
                .diskCacheStrategy(DiskCacheStrategy.SOURCE)
                .centerCrop()
                .into(holder.mImage);
        holder.mImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mItemClickListener != null) {
                    mItemClickListener.onItemClick(mPhotoList.get(position).id,holder.itemView);
                }
            }
        });
    }

    @Override
    public int getItemCount() {
        return mPhotoList == null ? 0:mPhotoList.size();
    }

    class PhotoViewHolder extends RecyclerView.ViewHolder{
        @BindView(R.id.iv_photo)
        ImageView mImage;
        @BindView(R.id.tv_title)
        TextView mTitle;
        public PhotoViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }
    }

    public interface OnItemClickListener {
        void onItemClick(String id,View view);
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        mItemClickListener = listener;
    }

}

4. 實(shí)現(xiàn)P層

P層提供兩個(gè)供V層調(diào)用的方法

  • getPhotoList(int state)
  • getSearchPhotoList(String query)

M層在P層的回調(diào)接口
presenter.listener.OnUnsplashPhotoListListener

public interface OnUnsplashPhotoListListener {
    void onLoadPhotoListSuccess(List<UnsplashPhotosList> photosList);

    void onLoadSearchPhotoListSuccess(UnsplashPhotoSearch photosList);

    void onLoadDataError(String error);
}

P層接口
presenter.UnsplashPhotoListPresenter

public interface UnsplashPhotoListPresenter {
    void getPhotoList(int state);
    void getSearchPhotoList(String query);
}

P層實(shí)現(xiàn)類
presenter.imp1.UnsplashPhotoListPresenterImp1

public class UnsplashPhotoListPresenterImp1 extends BasePresenter<UnsplashPhotoListView> implements UnsplashPhotoListPresenter,OnUnsplashPhotoListListener{

    private UnsplashPhotoListView mUnsplashPhotoListView;
    private UnsplashPhotoListModelImp1 mUnsplashPhotoListModelImp1;

    public UnsplashPhotoListPresenterImp1(UnsplashPhotoListView unsplashPhotoListView) {
        mUnsplashPhotoListView = unsplashPhotoListView;
        mUnsplashPhotoListModelImp1 = new UnsplashPhotoListModelImp1();
    }
    @Override
    public void getPhotoList(int state) {
        mUnsplashPhotoListView.onStartGetData();
        mUnsplashPhotoListModelImp1.setState(state);
        mUnsplashPhotoListModelImp1.loadPhotoList(this);
    }

    @Override
    public void getSearchPhotoList(String query) {
        mUnsplashPhotoListView.onStartGetData();
        mUnsplashPhotoListModelImp1.loadSearchPhotoList(this,query);
    }


    @Override
    public void onLoadPhotoListSuccess(List<UnsplashPhotosList> photosList) {
        mUnsplashPhotoListView.onGetPhotoSuccess(photosList);
    }

    @Override
    public void onLoadSearchPhotoListSuccess(UnsplashPhotoSearch photosList) {
        mUnsplashPhotoListView.onGetSearchPhotoSuccess(photosList);
    }

    @Override
    public void onLoadDataError(String error) {
        mUnsplashPhotoListView.onGetDataFailed(error);
    }
}

5. 圖片詳情界面

繁瑣的MVP各層代碼這里就不貼了,參照上面代碼實(shí)現(xiàn),這只寫下具體的一些方法。

  • 下載圖片
    下載圖片的功能寫在M層的實(shí)現(xiàn)類里。
    具體實(shí)現(xiàn)是兩個(gè)方法:
    • downloadPhoto(final OnUnsplashPhotoListener listener, String id, final String photoName)
      傳的參數(shù)分別為回調(diào)接口,圖片下載url,保存文件名。

    • DownloadImage(ResponseBody body,OnUnsplashPhotoListener listener,String photoName)
      傳的參數(shù)分別為網(wǎng)絡(luò)請(qǐng)求返回?cái)?shù)據(jù),回調(diào)接口,保存文件名。方法內(nèi)部實(shí)現(xiàn)了將返回的字節(jié)流寫入文件的功能。

model.UnsplashPhotoModelImp1

 public void downloadPhoto(final OnUnsplashPhotoListener listener, String id, final String photoName) {
       if (mTupianApi != null) {
           mTupianApi.getPhotoDownload(id)
                   .subscribeOn(Schedulers.io())
                   .map(new Function<ResponseBody, Boolean>() {
                       @Override
                       public Boolean apply(@NonNull ResponseBody responseBody) throws Exception {
                           return DownloadImage(responseBody,listener,photoName);
                       }
                   })
                   .observeOn(AndroidSchedulers.mainThread())
                   .subscribe(new Observer<Boolean>() {
                       @Override
                       public void onSubscribe(@NonNull Disposable d) {

                       }

                       @Override
                       public void onNext(@NonNull Boolean aBoolean) {
                            if (aBoolean) {
                                listener.onDownloadSuccess();
                            }
                       }

                       @Override
                       public void onError(@NonNull Throwable e) {
                           listener.onLoadDataError(e.toString());
                       }

                       @Override
                       public void onComplete() {

                       }
                   });
        }
    }

 private boolean DownloadImage(ResponseBody body,OnUnsplashPhotoListener listener,String photoName) {
        try {
            InputStream in;
            in = null;
            FileOutputStream out = null;

            try {
                in = body.byteStream();
                //String dir = Environment.getExternalStorageDirectory() + "/KTReader/";
                out = new FileOutputStream(MyApplication.getAppCacheDir()+ File.separator + photoName + ".jpg");
                byte[] buf = new byte[1024];

                while ((in.read(buf)) != -1) {
                    out.write(buf);
                }

            }
            catch (IOException e) {
                return false;
            }
            finally {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            }
            return true;

        } catch (IOException e) {
            listener.onLoadDataError(e.toString());
            return false;
        }
    }

在model層的實(shí)現(xiàn)類里還有一個(gè)獲取圖片大小的方法:
使用了Rxjava2的異步調(diào)用。

public void getPhotoSize(final OnUnsplashPhotoListener listener, final String urlString, final int pos) {
        Flowable.just(urlString)
                .subscribeOn(Schedulers.io())
                .map(new Function<String, Integer>() {
                    @Override
                    public Integer apply(@NonNull String s) throws Exception {
                        int size = -1;
                        try {
                            URL url = new URL(urlString);
                            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                            connection.setRequestMethod("GET");
                            size = connection.getContentLength();
                            return size;
                        }catch (Exception e) {
                            e.printStackTrace();
                        }
                        return size;
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(@NonNull Integer s) throws Exception {
                        listener.onLoadSizeSuccess(s, pos);
                    }
                });

    }

這里最終返回的大小會(huì)在View層的彈出框中呈現(xiàn),使得用戶可以根據(jù)自身需求下載不同質(zhì)量的圖片。

下面就說說view層的彈出框的寫法:

我們想要的功能是彈出對(duì)話框,然后呈現(xiàn)不同質(zhì)量圖片的大小,這就需要在對(duì)話框create之后更新視圖,這里我們選擇setAdapter的做法(還有一種我知道的是setView,然后更新view里的Textview等子布局),首先我們寫個(gè)簡(jiǎn)單的adapter:

    private String[] itemsSize = { "raw (大小正在計(jì)算中)","full (大小正在計(jì)算中)","regularl (大小正在計(jì)算中)","small (大小正在計(jì)算中)" };
    private String[] items = { "raw","full","regular","small" };
    ArrayAdapter<String> adapter;

final Context dialogContext = new ContextThemeWrapper(this,
                android.R.style.Theme_Light);
        final LayoutInflater dialogInflater = (LayoutInflater) dialogContext
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        adapter = new ArrayAdapter<String>(this,
                R.layout.item_dialog_list, itemsSize) {
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                if (convertView == null) {
                    convertView = dialogInflater.inflate(
                            R.layout.item_dialog_list, parent, false);
                }

                final TextView text1 = (TextView) convertView
                        .findViewById(R.id.tv);
                final String display = this.getItem(position);
                text1.setText(display);

                return convertView;
            }
        };

然后構(gòu)建AlertDialog時(shí)傳進(jìn)adapter:

AlertDialog.Builder listDialog = new AlertDialog.Builder(this)
                .setTitle("選擇要下載的圖片質(zhì)量:")
                .setAdapter(adapter, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        toast("已添加進(jìn)下載任務(wù),圖片質(zhì)量" + itemsSize[which] );
                        switch (which) {
                            case 0:
                                mPresenter.downLoadPhoto(mUnsplashPhoto.urls.raw,
                                        mUnsplashPhoto.id + "(" + items[which] + ")");
                                break;
                            case 1:
                                mPresenter.downLoadPhoto(mUnsplashPhoto.urls.full,
                                        mUnsplashPhoto.id + "(" + items[which] + ")");
                                break;
                            case 2:
                                mPresenter.downLoadPhoto(mUnsplashPhoto.urls.regular,
                                        mUnsplashPhoto.id + "(" + items[which] + ")");
                                break;
                            case 3:
                                mPresenter.downLoadPhoto(mUnsplashPhoto.urls.small,
                                        mUnsplashPhoto.id + "(" + items[which] + ")");
                                break;
                            default:
                                break;
                        }

                    }
                })
                .setPositiveButton("取消下載", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                    }
                });
        listDialog.show();

以上AlertDialog的構(gòu)建我們封裝為方法showListDialog()以方便調(diào)用;

最后我們?cè)谙鄳?yīng)的下載布局添加點(diǎn)擊事件讓P層調(diào)用M層的獲取大小方法并返回,然后更新AlertDialog的數(shù)據(jù):

@OnClick({R.id.button_collect, R.id.button_detail, R.id.button_download})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.button_download:
                mPresenter.getPhotoSize(mUnsplashPhoto.urls.raw,0);
                mPresenter.getPhotoSize(mUnsplashPhoto.urls.full,1);
                mPresenter.getPhotoSize(mUnsplashPhoto.urls.regular,2);
                mPresenter.getPhotoSize(mUnsplashPhoto.urls.small,3);
                showListDialog();
                break;
        }
    }

最后我們?cè)诔晒卣{(diào)的函數(shù)里改變adapter的數(shù)據(jù),然后更新adapter:

@Override
    public void onGetSizeSuccess(int size,int pos) {
        itemsSize[pos] = String.format(items[pos] + "       (圖片大約" + size/1024 + "K)");;
        adapter.notifyDataSetChanged();
    }

至此,我們的下載選擇彈出框功能就完成了。

而圖片的詳細(xì)內(nèi)容顯示我們?yōu)榱朔奖阃瑯邮褂肁lertDialog完成,這次簡(jiǎn)單的使用他的setItems方法,然后傳進(jìn)對(duì)應(yīng)數(shù)據(jù)就可以簡(jiǎn)單顯示了。

private void showPhotoDetail() {
        String[] detailItems = {
//                "標(biāo)題:  " + mUnsplashPhoto.location.title,
//                "作者:  " + mUnsplashPhoto.location.name,
                "拍攝時(shí)間:  " + mUnsplashPhoto.createdAt,
                "拍攝設(shè)備:  " + mUnsplashPhoto.exif.make,
                "寬度:  " + mUnsplashPhoto.width,
                "高度:  " + mUnsplashPhoto.height,
                "保存路徑:  " + MyApplication.getAppCacheDir()+ File.separator + mUnsplashPhoto.id + ".jpg"};
        AlertDialog.Builder builder = new AlertDialog.Builder(this)
                .setTitle("Photo 詳情")
                .setItems(detailItems, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                    }
                })
                .setPositiveButton("確定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                    }
                });
        builder.show();
    }

以上,便是KTReader的圖片模塊,功能略單一,代碼也只是小白水平,如有批評(píng)建議,歡迎指正。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容