依赖
useLibrary 'org.apache.http.legacy'
//Volley依赖 implementation 'com.android.volley:volley:1.1.1-rc1' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.github.ikidou:TypeBuilder:1.0' /** * 日志打印库 */ implementation 'com.orhanobut:logger:2.2.0' //图片加载库 implementation 'com.github.bumptech.glide:glide:3.8.0' //大图浏览库 implementation 'com.ycjiang:ImagePreview:2.1.7' //九宫格图片展示 implementation 'com.jaeger.ninegridimageview:library:1.1.1' //图片加载库 implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
ICallBack 接口
public interface ICallBack { void success(List<Comment> data); void failed(String msg); }
ICommentTask 接口
public interface ICommentTask { void getCommentList(String url,ICallBack callBack); }
CommentContract 接口
public interface CommentContract { public interface ICommentPresenter extends BasePresenter { void getCommentList(); } public interface ICommentView extends BaseView<CommentPresenter> { /** * 展示数据 * @param list */ void showData(List<Comment> list); void showError(String string); } }
CommentTask
public class CommentTask implements ICommentTask { @Override public void getCommentList(String url, final ICallBack callBack) { RequestQueue queue = Volley.newRequestQueue(App.context); StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() { @Override public void onResponse(String response) { Logger.json(response); RootData<Comment> data = fromJsonArray(response, Comment.class); callBack.success(data.data); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { callBack.failed(error.getMessage()); } }); queue.add(request); } /** * 解析泛型类 * @param reader * @param clazz * @param <T> * @return */ public static <T> RootData<T> fromJsonArray(String reader, Class<T> clazz) { Type type = TypeBuilder .newInstance(RootData.class) .addTypeParam(clazz) .build(); return new Gson().fromJson(reader, type); } }
CommentPresenter
public class CommentPresenter implements ICommentPresenter { CommentTask task; ICommentView view; /** * 构造方法 * * @param activity */ public CommentPresenter(CommentActivity activity) { task = new CommentTask(); view = activity; } /** * 首次加载 */ public void getFirstData(){ getCommentList(0,10,false); } /** * 加载更多数据 * @param page */ public void getMoreData(int page){ getCommentList(1,10,false); } /** * 刷新数据 */ public void refreshData(){ getCommentList(0,10,true); } /** * 获取列表 * * @param page * @param count */ @Override public void getCommentList(final int page, int count, final boolean isRefresh) { String url = "http://39.108.3.12:3000/v1/comment?restaurant_id=32&offset=" + page + "&limit=" + count; task.getCommentList(url, new ICallBack() { @Override public void success(List<Comment> data) { if (view != null) { if (page == 0) { if (isRefresh) { view.showRefreshData(data); } else { view.showFirstData(data); } } else { view.showMoreData(data); } } } @Override public void failed(String msg) { if (view != null) view.showError(msg); } }); } @Override public void start() { } /** * 销毁,防止内存泄漏 */ @Override public void onDestroy() { view = null; } }
ICommentPresenter
public interface ICommentPresenter extends BasePresenter { void getCommentList(int page,int count,boolean isRefresh); }
CommentActivity
public class CommentActivity extends AppCompatActivity implements ICommentView { CommentPresenter presenter; ListView listView; CommentAdapter adapter=new CommentAdapter(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView = (ListView) findViewById(R.id.lv_comment); setPresenter(new CommentPresenter(this)); presenter.getFirstData();//首次加载 /** * 在XListView中的加载更多监听中调用 */ presenter.getMoreData(1);//加载更多 /** * 在XListView中的刷新监听中调用 */ presenter.refreshData();//刷新数据 } /** * 首次加载 * @param list */ @Override public void showFirstData(List<Comment> list) { //TODO 处理列表 Logger.d(list); adapter.addDatas(list); listView.setAdapter(adapter); } /** * 加载更过 * @param list */ @Override public void showMoreData(List<Comment> list) { adapter.addDatas(list); listView.setAdapter(adapter); } /** * 下拉刷新 * @param list */ @Override public void showRefreshData(List<Comment> list) { adapter.clearData();//清除原有数据 //添加数据 adapter.addDatas(list); listView.setAdapter(adapter); } /** * 注入P * * @param presenter */ @Override public void setPresenter(CommentPresenter presenter) { this.presenter = presenter; } /** * 网络请求错误的时候执行 * * @param string */ @Override public void showError(String string) { //TODO 弹出错误信息 } }
ICommentView
public interface ICommentView extends BaseView<CommentPresenter> { /** * 展示数据 * @param list */ void showFirstData(List<Comment> list); void showMoreData(List<Comment> list); void showRefreshData(List<Comment> list); void showError(String string); }
适配器
public class CommentAdapter extends BaseAdapter { public static final int TYPE_TEXT = 0; public static final int TYPE_TEXT_IMAGE = 1; public static final int TYPE_COUNT = 2; private List<Comment> comments; private Context context; private LayoutInflater inflater; private ImageLoader loader; DisplayImageOptions optionsCircle; DisplayImageOptions options; public CommentAdapter(Context context) { this.comments = new ArrayList<>(); this.context = context; rthis.inflater = LayoutInflater.from(context); loader = ImageLoader.getInstance(); optionsCircle = new DisplayImageOptions.Builder() .showImageOnLoading(R.mipmap.ic_launcher_round) // resource or drawable .showImageForEmptyUri(R.mipmap.ic_launcher_round) // resource or drawable .showImageOnFail(R.mipmap.ic_launcher_round) // resource or drawable .cacheInMemory(true) // default .cacheOnDisk(true) // default .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default .bitmapConfig(Bitmap.Config.RGB_565) // default .displayer(new CircleBitmapDisplayer(Color.BLACK, 1)) // default .build(); options = new DisplayImageOptions.Builder() .showImageOnLoading(R.mipmap.ic_launcher_round) // resource or drawable .showImageForEmptyUri(R.mipmap.ic_launcher_round) // resource or drawable .showImageOnFail(R.mipmap.ic_launcher_round) // resource or drawable .cacheInMemory(true) // default .cacheOnDisk(true) // default .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default .bitmapConfig(Bitmap.Config.RGB_565) // default .displayer(new SimpleBitmapDisplayer()) // default .build(); } /** * 添加数据 * * @param list */ public void addDatas(List<Comment> list) { this.comments.addAll(list); notifyDataSetChanged(); } /** * 清除数据 */ public void clearData(){ this.comments.clear(); } @Override public int getCount() { return comments.size(); } @Override public Comment getItem(int i) { return comments.get(i); } @Override public long getItemId(int i) { return i; } @Override public int getItemViewType(int position) { Comment comment = getItem(position); if (comment.pic_url != null && !comment.pic_url.isEmpty()) { return TYPE_TEXT_IMAGE; } else { return TYPE_TEXT; } } @Override public int getViewTypeCount() { return TYPE_COUNT; } @Override public View getView(int i, View view, ViewGroup viewGroup) { ViewHolderText holderText = null; ViewHolderTextImage holderTextImage = null; int type = getItemViewType(i); final Comment comment = getItem(i); if (view == null) { if (type == TYPE_TEXT) { view = inflater.inflate(R.layout.item_text, viewGroup, false); holderText = new ViewHolderText(view); view.setTag(holderText); } else if (type == TYPE_TEXT_IMAGE) { view = inflater.inflate(R.layout.item_text_image, viewGroup, false); holderTextImage = new ViewHolderTextImage(view); view.setTag(holderTextImage); } } else { if (type == TYPE_TEXT) { holderText = (ViewHolderText) view.getTag(); } else if (type == TYPE_TEXT_IMAGE) { holderTextImage = (ViewHolderTextImage) view.getTag(); } } if (type == TYPE_TEXT) { holderText.user_name.setText(comment.user_name); holderText.user_comment.setText(comment.comment_data); holderText.user_comment_time.setText(comment.comment_time); loader.displayImage(comment.avatar, holderText.user_icon, optionsCircle); } else if (type == TYPE_TEXT_IMAGE) { holderTextImage.user_name.setText(comment.user_name); holderTextImage.user_comment.setText(comment.comment_data); holderTextImage.user_comment_time.setText(comment.comment_time); final NineGridImageView nineGridImageView=holderTextImage.user_images; NineGridImageViewAdapter<String> adapter = new NineGridImageViewAdapter<String>() { @Override protected void onDisplayImage(Context context, ImageView imageView, String s) { loader.displayImage(s, imageView, options); } @Override protected void onItemImageClick(Context context, ImageView imageView, int index, List<String> list) { super.onItemImageClick(context, imageView, index, list); List<PhotoInfo> infos = new ArrayList<>(); for (String url : comment.pic_url) { infos.add(new PhotoInfo(url)); } /** * 拼接集合 */ computeBoundsBackward(nineGridImageView,infos); /** * 跳转 */ GPreviewBuilder.from((Activity) context) .setData(infos) .setCurrentIndex(index)// .setType(GPreviewBuilder.IndicatorType.Number) .start();//启动 } }; holderTextImage.user_images.setAdapter(adapter); holderTextImage.user_images.setImagesData(comment.pic_url); } return view; } /** * 只是文字 */ static class ViewHolderText { TextView user_name; TextView user_comment; TextView user_comment_time; ImageView user_icon; public ViewHolderText(View view) { user_name = (TextView) view.findViewById(R.id.user_name); user_comment = (TextView) view.findViewById(R.id.user_comment); user_comment_time = (TextView) view.findViewById(R.id.user_comment_time); user_icon = (ImageView) view.findViewById(R.id.user_icon); } } /** * 图片加文字 */ static class ViewHolderTextImage extends ViewHolderText { NineGridImageView user_images; public ViewHolderTextImage(View view) { super(view); this.user_images = (NineGridImageView) view.findViewById(R.id.user_images); } } /** * 查找信息 * @param user_images 九宫格控件 * @param list 数据列表 */ private void computeBoundsBackward(NineGridImageView user_images, List<PhotoInfo> list) { for (int i = 0; i < user_images.getChildCount(); i++) { View itemView = user_images.getChildAt(i);// Rect bounds = new Rect(); if (itemView != null) { ImageView thumbView = (ImageView) itemView; thumbView.getGlobalVisibleRect(bounds); } list.get(i).mBounds = bounds; } } }
PhotoInfo
public class PhotoInfo implements IThumbViewInfo { public String url; //图片地址(必须) public Rect mBounds; // 记录坐标(必须) public String user = "用户字段";//可选 public String videoUrl;//必须 public PhotoInfo(String url) { this.url = url; } protected PhotoInfo(Parcel in) { this.url = in.readString(); this.mBounds = in.readParcelable(Rect.class.getClassLoader()); this.user = in.readString(); this.videoUrl = in.readString(); } @Override public String getUrl() { return url; } @Override public Rect getBounds() { return mBounds; } @Nullable @Override public String getVideoUrl() { return videoUrl; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.url); dest.writeParcelable(this.mBounds, flags); dest.writeString(this.user); dest.writeString(this.videoUrl); } public static final Creator<PhotoInfo> CREATOR = new Creator<PhotoInfo>() { @Override public PhotoInfo createFromParcel(Parcel source) { return new PhotoInfo(source); } @Override public PhotoInfo[] newArray(int size) { return new PhotoInfo[size]; } }; }
TestImageLoader
public class TestImageLoader implements IZoomMediaLoader { @Override public void displayImage(@NonNull Fragment context,@NonNull String path, final@NonNull MySimpleTarget<Bitmap> simpleTarget) { Glide.with(context).load(path).asBitmap() .centerCrop() .diskCacheStrategy(DiskCacheStrategy.SOURCE) .error(R.mipmap.ic_launcher) .into(new SimpleTarget<Bitmap>() { @Override public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) { simpleTarget.onResourceReady(resource); } @Override public void onLoadStarted(Drawable placeholder) { super.onLoadStarted(placeholder); simpleTarget.onLoadStarted(); } @Override public void onLoadFailed(Exception e, Drawable errorDrawable) { super.onLoadFailed(e, errorDrawable); simpleTarget.onLoadFailed(errorDrawable); } }); } @Override public void onStop(@NonNull Fragment context) { Glide.with(context).onStop(); } @Override public void clearMemory(@NonNull Context c) { Glide.get(c).clearMemory(); } }
AntGridView
public class AntGridView extends GridView { public AntGridView(Context context) { super(context); } public AntGridView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } }
AntListView
public class AntListView extends ListView{ public AntListView(Context context) { super(context); } public AntListView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } }
App
public class App extends Application { public static Context context; @Override public void onCreate() { super.onCreate(); context = this; //日志库初始化 Logger.addLogAdapter(new AndroidLogAdapter(){ @Override public boolean isLoggable(int priority, @Nullable String tag) { //todo 关闭日志 返回 false return super.isLoggable(priority, tag); } } ); //大图浏览初始化 ZoomMediaLoader.getInstance().init(new TestImageLoader()); //图片加载库初始化 ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this) .build(); ImageLoader.getInstance().init(config); } }
BasePresenter
public interface BasePresenter { /** * 开始执行 */ void start(); /** * 销毁正在的操作,防止内存泄漏 */ void onDestroy(); }
BaseView
public interface BaseView<T> { /** *P注入V层 * @param presenter */ void setPresenter(T presenter); }
MainActivity
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ICommentTask task=new CommentTask(); task.getCommentList("http://39.108.3.12:3000/v1/comment?restaurant_id=32&offset=0&limit=5", new ICallBack() { @Override public void success(List<Comment> data) { Log.d(getLocalClassName(),data.toString()); } @Override public void failed(String msg) { } }); } @Override protected void onDestroy() { super.onDestroy(); } }
布局
<ListView android:id="@+id/lv_comment" android:divider="@color/colorPrimaryDark" android:dividerHeight="1dp" android:layout_width="match_parent" android:layout_height="wrap_content" />
<ImageView android:id="@+id/user_icon" android:layout_width="50dp" android:layout_height="50dp" android:src="@mipmap/ic_launcher" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_toRightOf="@id/user_icon" > <TextView android:id="@+id/user_name" android:textColor="@android:color/black" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:layout_below="@id/user_name" android:id="@+id/user_comment" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:layout_alignParentRight="true" android:layout_alignBaseline="@id/user_name" android:id="@+id/user_comment_time" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>
<ImageView android:id="@+id/user_icon" android:layout_width="50dp" android:layout_height="50dp" android:src="@mipmap/ic_launcher" /> <RelativeLayout android:id="@+id/layout_comment" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_toRightOf="@id/user_icon"> <TextView android:id="@+id/user_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@android:color/black" /> <TextView android:id="@+id/user_comment" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/user_name" /> <TextView android:id="@+id/user_comment_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBaseline="@id/user_name" android:layout_alignParentRight="true" /> </RelativeLayout> <com.jaeger.ninegridimageview.NineGridImageView android:id="@+id/user_images" android:layout_below="@id/layout_comment" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="16dp" app:imgGap="4dp" app:showStyle="fill" app:singleImgSize="120dp" />