Android练手小项目(KTReader)基于mvp架构(五)

上路传送眼:

Android练手小项目(KTReader)基于mvp架构(四)

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

上篇文章中我们完成了豆瓣模块。
而这次我们要做的的就是图片模块。

效果图奉上:


效果图.gif

总览一下图片模块我们完成的功能有:

  • 图片浏览(三种排序方式)
  • 图片关键字搜索
  • 图片详情查看
  • 图片下载(可查看和选择图片质量)
  • 下拉刷新,上拉加载

所用到的一些知识点有:

  • Rxjava2 + Retrofit2 + OKhttp3 实现网络请求
  • 下载文件的FileOutputStream用法
  • 下载文件的大小获取回调
  • AlertDialog的适配器视图和Items视图
  • Spinner的简单使用
  • RecycleView的瀑布流布局实现和踩坑(item重排)
  • SwipeRefreshLayout布局

可完善和加强的内容或功能有:

  • 收藏图片
  • 图片详情页的美化
  • 下载列表的实现
  • 下载图片时通知栏的进度提醒
  • 随意推荐一张图片

大体上介绍完毕之后,下面就说说具体的实现:

1. API的获取

想要做一个图片模块,数据是必不可少的,这里我们直接使用免费的api就行了,话说我百度出来的图片api为啥都是美女图啊喂,虽然很福利!为嘛找个综合图片的api那么麻烦啊,总之找了好久终于找到了个好用的api,分享给大家:Unsplash(唯一的不足就是没有中文的开发者文档,不过其实里面的英语挺好懂的,实在不行还可以划词翻译嘛)。
另外附上我找资源时发现的一个好东西,以后找api时可以直接先在这搜:

总之,注册好开发者账号和看了开发者文档后就可以直接写接口了。

api.TupianApi

public interface TupianApi {
    /*
     *Get a single page from the list of all photos.
     * 参数分别为appId,页码,每页个数,排序规则 (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类里添加个获取图片服务的方法
utils.RetrofitManager

private TupianApi tupianApi;
public TupianApi getTupianService(String url) {
        if (tupianApi == null) {
            tupianApi = new Retrofit.Builder()
                    .baseUrl(url) //必须以‘/’结尾
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//使用RxJava2作为CallAdapter
                    .client(client)//如果没有添加,那么retrofit2会自动给我们添加了一个。
                    .addConverterFactory(GsonConverterFactory.create())//Retrofit2可以帮我们自动解析返回数据,
                    .build().create(TupianApi.class);
        }
        return tupianApi;
    }

2. 实现M层

实体类老规矩直接GsonFormat生成,不过Unsplash返回的直接是数组类型,以至于工具生成的实体类为数组里的具体项,因此我们在写api接口时类型要为List类型,这样才能解析出列表数据。

  • M层的接口
    我们需要的功能为加载查询的关键字图片数据和默认加载数据
    model.UnsplashPhotoListModel
public interface UnsplashPhotoListModel {
    void loadSearchPhotoList(OnUnsplashPhotoListListener listener,String query);
    void loadPhotoList(OnUnsplashPhotoListListener listener);
}
  • M层的实现类
    实现类里我们定义了挺多变量,是为了控制获取的数据数量和类型。
    分别有页码,每页图片数量,和排序方式等。
    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. 实现V层

首先确定我们要实现的功能,大致分为4种:

  • 开始获取数据
  • 获取默认数据成功
  • 获取查询数据成功
  • 获取数据失败

详情代码如下
view.UnsplashPhotoListView

public interface UnsplashPhotoListView {
    void onStartGetData();

    void onGetPhotoSuccess(List<UnsplashPhotosList> photosList);

    void onGetSearchPhotoSuccess(UnsplashPhotoSearch photosList);

    void onGetDataFailed(String error);
}

然后可以写具体的界面
这里我们同样分析一下界面需要如何实现:
首先我们需要个搜索栏和选择栏,然后再来个recycleview来加载图片,为了美观我们采用瀑布流的布局,当内容拉到底部时,我们要加载更多的图片,这是我们就需要个变量标识我们需要加载更多的是用户自己搜索的图片还是默认加载的图片(哪种排序方式),同时这个标识用于传递给P层通知它要加载的数据类型。

分析完成后就是具体代码的实现:
ui.fragment.UnsplashListFragment

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

    private UnsplashListAdapter unsplashListAdapter;
    private int STATE = 0;//当前的状态
    //拥有的状态 :latest", "oldest", "popular",搜索状态。
    private final int STATE_LATEST = 0;
    private final int STATE_OLDEST = 1;
    private final int STATE_POPULAR = 2;
    private final int STATE_SEARCH = 3;
    //记录搜索状态下的搜索项
    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();
        //监听 SwipeRefreshLayout 事件 上拉就调用ReLoad方法。
        mContentSwipe.setOnRefreshListener(this);
        //监听 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;
            }
        });
        //初始化加载数据 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监听事件 点击item,跳转到相应的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);
        }
    };

    //重新加载数据 ,首先清空数据,然后根据当前状态获取数据
    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("");
    }
}

这里有个坑,当RecycleView设置为StaggeredGridLayoutManager布局方式时,我们会发现RecycleView的item项会变位置,原来是每次其高度都随机导致,这里我们在adapter内部维护一个高度数组,让内部的item有着不变的随机高度。

其具体实现代码我也贴出 如下:
adapter.UnsplashListAdapter

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

    private List<UnsplashPhotosList> mPhotoList;
    private Context mContext;
    private OnItemClickListener mItemClickListener;
    //用于每个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));
        }
        //应该局部 刷新 ,有个bug TODO: 2017/6/11  bug
        //notifyItemRangeChanged(currentPos,unsplashPhotosList.size());
        notifyDataSetChanged();
    }

    //清空数据
    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. 实现P层

P层提供两个供V层调用的方法

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

M层在P层的回调接口
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层实现类
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各层代码这里就不贴了,参照上面代码实现,这只写下具体的一些方法。

  • 下载图片
    下载图片的功能写在M层的实现类里。
    具体实现是两个方法:
    • downloadPhoto(final OnUnsplashPhotoListener listener, String id, final String photoName)
      传的参数分别为回调接口,图片下载url,保存文件名。

    • DownloadImage(ResponseBody body,OnUnsplashPhotoListener listener,String photoName)
      传的参数分别为网络请求返回数据,回调接口,保存文件名。方法内部实现了将返回的字节流写入文件的功能。

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层的实现类里还有一个获取图片大小的方法:
使用了Rxjava2的异步调用。

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);
                    }
                });

    }

这里最终返回的大小会在View层的弹出框中呈现,使得用户可以根据自身需求下载不同质量的图片。

下面就说说view层的弹出框的写法:

我们想要的功能是弹出对话框,然后呈现不同质量图片的大小,这就需要在对话框create之后更新视图,这里我们选择setAdapter的做法(还有一种我知道的是setView,然后更新view里的Textview等子布局),首先我们写个简单的adapter:

    private String[] itemsSize = { "raw (大小正在计算中)","full (大小正在计算中)","regularl (大小正在计算中)","small (大小正在计算中)" };
    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;
            }
        };

然后构建AlertDialog时传进adapter:

AlertDialog.Builder listDialog = new AlertDialog.Builder(this)
                .setTitle("选择要下载的图片质量:")
                .setAdapter(adapter, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        toast("已添加进下载任务,图片质量" + 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的构建我们封装为方法showListDialog()以方便调用;

最后我们在相应的下载布局添加点击事件让P层调用M层的获取大小方法并返回,然后更新AlertDialog的数据:

@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;
        }
    }

最后我们在成功回调的函数里改变adapter的数据,然后更新adapter:

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

至此,我们的下载选择弹出框功能就完成了。

而图片的详细内容显示我们为了方便同样使用AlertDialog完成,这次简单的使用他的setItems方法,然后传进对应数据就可以简单显示了。

private void showPhotoDetail() {
        String[] detailItems = {
//                "标题:  " + mUnsplashPhoto.location.title,
//                "作者:  " + mUnsplashPhoto.location.name,
                "拍摄时间:  " + mUnsplashPhoto.createdAt,
                "拍摄设备:  " + 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的图片模块,功能略单一,代码也只是小白水平,如有批评建议,欢迎指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容