成长页面实现帖子列表,详情,文字和九宫格图片展示

This commit is contained in:
BA7LZD 2020-05-26 11:42:10 +08:00
parent 5937f51951
commit 579c02be95
14 changed files with 552 additions and 24 deletions

View File

@ -50,7 +50,14 @@ dependencies {
implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
// fragment快速实现
implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
//imageView
implementation 'de.hdodenhof:circleimageview:3.1.0'
//
implementation 'com.lcodecorex:tkrefreshlayout:1.0.7'
//
implementation 'com.lzy.widget:ninegridview:0.2.0'
//sdk
implementation 'com.tencent.qcloud:cosxml:5.4.31'
//
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'

View File

@ -4,7 +4,10 @@
package="com.yuxihan.sdu">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:name=".comm.SDUApp"
@ -12,6 +15,7 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:maxAspectRatio="2.4"
android:usesCleartextTraffic="true"
android:resizeableActivity="true"
android:supportsRtl="true"
android:theme="@style/AppTheme"
@ -46,8 +50,9 @@
android:configChanges="orientation|screenSize|keyboardHidden"
android:screenOrientation="fullSensor" />
<activity android:name=".gsv.SimplePlayer" />
<activity android:name=".gsv.SimpleDetailActivityMode2"
android:theme="@style/DetailTheme"/>
<activity
android:name=".gsv.SimpleDetailActivityMode2"
android:theme="@style/DetailTheme" />
<activity android:name=".comm.BaseActivity" />
</application>

View File

@ -1,8 +1,15 @@
package com.yuxihan.sdu.comm;
import android.app.Application;
import android.content.Context;
import android.graphics.Bitmap;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.lzy.ninegrid.NineGridView;
import com.yuxihan.sdu.BuildConfig;
import com.yuxihan.sdu.R;
import com.yuxihan.sdu.comm.network.LoggingInterceptor;
import java.io.IOException;
@ -26,9 +33,34 @@ public class SDUApp extends Application {
private void initApp() {
initRetrofit();
initNineGrid();
}
/**
* 初始化九宫格
*/
private void initNineGrid() {
NineGridView.setImageLoader(new GlideImageLoader());
}
/** Glide 加载 */
private static class GlideImageLoader implements NineGridView.ImageLoader {
@Override
public void onDisplayImage(Context context, ImageView imageView, String url) {
Glide.with(context).load(url)
.placeholder(R.color.divider_gray)
.error(R.color.divider_gray)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageView);
}
@Override
public Bitmap getCacheImage(String url) {
return null;
}
}
public static Retrofit getRetrofit() {
if (mRetrofit == null) {
initRetrofit();

View File

@ -0,0 +1,24 @@
package com.yuxihan.sdu.comm.util;
import android.util.SparseArray;
import android.view.View;
public class CommViewHolder {
// I added a generic return type to reduce the casting noise in client code
@SuppressWarnings("unchecked")
public static <T extends View> T get(View view, int id) {
SparseArray<View> viewHolder = (SparseArray<View>) view.getTag();
if (viewHolder == null) {
viewHolder = new SparseArray<View>();
view.setTag(viewHolder);
}
View childView = viewHolder.get(id);
if (childView == null) {
childView = view.findViewById(id);
viewHolder.put(id, childView);
}
return (T) childView;
}
}

View File

@ -0,0 +1,62 @@
package com.yuxihan.sdu.comm.util;
import java.util.Date;
public class TimeConvert {
private static final int seconds_of_1minute = 60;
private static final int seconds_of_30minutes = 30 * 60;
private static final int seconds_of_1hour = 60 * 60;
private static final int seconds_of_1day = 24 * 60 * 60;
private static final int seconds_of_15days = seconds_of_1day * 15;
private static final int seconds_of_30days = seconds_of_1day * 30;
private static final int seconds_of_6months = seconds_of_30days * 6;
private static final int seconds_of_1year = seconds_of_30days * 12;
/**
* createTime距离如今经过的时间分为
* 刚刚1-29分钟前半小时前1-23小时前1-14天前半个月前1-5个月前半年前1-xxx年前
*/
public static String getTimeElapse(long createTime) {
long nowTime = new Date().getTime() / 1000;
//elapsedTime是发表和如今的间隔时间
long elapsedTime = nowTime - createTime;
if (elapsedTime < seconds_of_1minute) {
return "刚刚";
}
if (elapsedTime < seconds_of_30minutes) {
return elapsedTime / seconds_of_1minute + "分钟前";
}
if (elapsedTime < seconds_of_1hour) {
return "半小时前";
}
if (elapsedTime < seconds_of_1day) {
return elapsedTime / seconds_of_1hour + "小时前";
}
if (elapsedTime < seconds_of_15days) {
return elapsedTime / seconds_of_1day + "天前";
}
if (elapsedTime < seconds_of_30days) {
return "半个月前";
}
if (elapsedTime < seconds_of_6months) {
return elapsedTime / seconds_of_30days + "月前";
}
if (elapsedTime < seconds_of_1year) {
return "半年前";
}
return elapsedTime / seconds_of_1year + "年前";
}
}

View File

@ -0,0 +1,125 @@
package com.yuxihan.sdu.data.model;
import java.util.ArrayList;
public class PostDetailBean {
private String postId;
private String userName;
private String userHead;
private String userId;
private long postTime;
private String postTextContent;
private String postVideoUrl;
private ArrayList<String> postPicUrls;
private int postLikeCount;
private boolean isLikedByCurAccount;
private int commentCount;
public PostDetailBean() {
}
public PostDetailBean(String postId, String userName, String userHead, String userId,
long postTime, String postTextContent, String postVideoUrl,
ArrayList<String> postPicUrls, int postLikeCount,
boolean isLikedByCurAccount,int commentCount) {
this.postId = postId;
this.userName = userName;
this.userHead = userHead;
this.userId = userId;
this.postTime = postTime;
this.postTextContent = postTextContent;
this.postVideoUrl = postVideoUrl;
this.postPicUrls = postPicUrls;
this.postLikeCount = postLikeCount;
this.isLikedByCurAccount = isLikedByCurAccount;
this.commentCount = commentCount;
}
public String getPostId() {
return postId;
}
public void setPostId(String postId) {
this.postId = postId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserHead() {
return userHead;
}
public void setUserHead(String userHead) {
this.userHead = userHead;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public long getPostTime() {
return postTime;
}
public void setPostTime(long postTime) {
this.postTime = postTime;
}
public String getPostTextContent() {
return postTextContent;
}
public void setPostTextContent(String postTextContent) {
this.postTextContent = postTextContent;
}
public String getPostVideoUrl() {
return postVideoUrl;
}
public void setPostVideoUrl(String postVideoUrl) {
this.postVideoUrl = postVideoUrl;
}
public ArrayList<String> getPostPicUrls() {
return postPicUrls;
}
public void setPostPicUrls(ArrayList<String> postPicUrls) {
this.postPicUrls = postPicUrls;
}
public int getPostLikeCount() {
return postLikeCount;
}
public void setPostLikeCount(int postLikeCount) {
this.postLikeCount = postLikeCount;
}
public boolean isLikedByCurAccount() {
return isLikedByCurAccount;
}
public void setLikedByCurAccount(boolean likedByCurAccount) {
isLikedByCurAccount = likedByCurAccount;
}
public int getCommentCount() {
return commentCount;
}
public void setCommentCount(int commentCount) {
this.commentCount = commentCount;
}
}

View File

@ -1,6 +1,7 @@
package com.yuxihan.sdu.ui.grow;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -12,10 +13,15 @@ import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.lcodecore.tkrefreshlayout.RefreshListenerAdapter;
import com.lcodecore.tkrefreshlayout.TwinklingRefreshLayout;
import com.yuxihan.sdu.R;
import com.yuxihan.sdu.comm.BaseFragment;
import com.yuxihan.sdu.data.model.PostDetailBean;
import com.yuxihan.sdu.ui.grow.adapter.GrowListAdapter;
import java.util.ArrayList;
public class GrowFragment extends BaseFragment {
private GrowModel growModel;
@ -30,15 +36,41 @@ public class GrowFragment extends BaseFragment {
}
private void initView(View root) {
TwinklingRefreshLayout refreshLayout = root.findViewById(R.id.refreshLayout);
refreshLayout.setOnRefreshListener(new RefreshListenerAdapter() {
@Override
public void onRefresh(final TwinklingRefreshLayout refreshLayout) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
refreshLayout.finishRefreshing();
}
}, 2000);
}
@Override
public void onLoadMore(final TwinklingRefreshLayout refreshLayout) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
refreshLayout.finishLoadmore();
}
}, 2000);
}
});
RecyclerView growListRV = root.findViewById(R.id.rv_grow_list);
growListRV.setLayoutManager(new LinearLayoutManager(getContext()));
GrowListAdapter growListAdapter = new GrowListAdapter();
growListRV.setHasFixedSize(true);
GrowListAdapter growListAdapter = new GrowListAdapter(null);
growListRV.setAdapter(growListAdapter);
growModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
growModel.getPostListLD().observe(getViewLifecycleOwner(), new Observer<ArrayList<PostDetailBean>>() {
@Override
public void onChanged(@Nullable String s) {
growListAdapter.updateData(s);
public void onChanged(ArrayList<PostDetailBean> postDetailBeans) {
growListAdapter.updateData(postDetailBeans);
}
});
}
}

View File

@ -1,19 +1,46 @@
package com.yuxihan.sdu.ui.grow;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.yuxihan.sdu.data.model.PostDetailBean;
import java.util.ArrayList;
public class GrowModel extends ViewModel {
private MutableLiveData<String> mText;
private MutableLiveData<ArrayList<PostDetailBean>> postListLD;
public GrowModel() {
mText = new MutableLiveData<>();
mText.setValue("This is home fragment");
postListLD = new MutableLiveData<>();
ArrayList<String> picUrlList = new ArrayList<>();
picUrlList.add("http://t9.baidu.com/it/u=188672807,2175104643&fm=79&app=86&f=JPEG?w=1280&h=853");
picUrlList.add("http://t9.baidu.com/it/u=3989902316,3793481456&fm=79&app=86&f=JPEG?w=1280&h=853");
picUrlList.add("http://t9.baidu.com/it/u=1038289730,4068396403&fm=79&app=86&f=JPEG?w=1280&h=853");
picUrlList.add("http://t8.baidu.com/it/u=3346148719,3315269675&fm=79&app=86&f=JPEG?w=666&h=1000");
picUrlList.add("http://t7.baidu.com/it/u=1263664584,1982855955&fm=79&app=86&f=JPEG?w=534&h=800");
picUrlList.add("http://t7.baidu.com/it/u=2832630561,1836266954&fm=79&app=86&f=JPEG?w=667&h=1000");
picUrlList.add("http://t8.baidu.com/it/u=3647266255,2679682355&fm=79&app=86&f=JPEG?w=667&h=1000");
picUrlList.add("http://t7.baidu.com/it/u=558764796,419343714&fm=79&app=86&f=JPEG?w=667&h=1000");
picUrlList.add("http://t7.baidu.com/it/u=4047929277,3427505730&fm=79&app=86&f=JPEG?w=667&h=1000");
ArrayList<PostDetailBean> postList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
postList.add(new PostDetailBean("0000" + i, "雨曦大魔王" + i, "http://jzvd-pic.nathen" +
".cn/jzvd-pic/1bb2ebbe-140d-4e2e-abd2-9e7e564f71ac.png", "1" + i,
1590460786L,
i + ":图片大图预览效果是一个库工程,方便导入使用。\n" +
"对不同张数图片进行的适配。\n" +
"根据NineGridView的属性来控制不同的显示模式\n" +
"向QQ空间那样大于9张图片的时候以+号显示\n" +
"图片加载非常的灵活。\n" +
"大图加载的时候有预览动画,大图的尺寸根据原图的尺度", null, new ArrayList<String>(picUrlList.subList(0, i % 10)), 15 + i, true,0));
}
postListLD.setValue(postList);
}
public LiveData<String> getText() {
return mText;
public MutableLiveData<ArrayList<PostDetailBean>> getPostListLD() {
return postListLD;
}
}

View File

@ -1,28 +1,89 @@
package com.yuxihan.sdu.ui.grow.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.lzy.ninegrid.ImageInfo;
import com.lzy.ninegrid.NineGridView;
import com.lzy.ninegrid.NineGridViewAdapter;
import com.yuxihan.sdu.R;
import com.yuxihan.sdu.comm.util.CommViewHolder;
import com.yuxihan.sdu.comm.util.TimeConvert;
import com.yuxihan.sdu.data.model.PostDetailBean;
import java.util.ArrayList;
import de.hdodenhof.circleimageview.CircleImageView;
public class GrowListAdapter extends RecyclerView.Adapter {
private ArrayList<PostDetailBean> mDataSet;
private Context mContext;
public GrowListAdapter(ArrayList<PostDetailBean> myDataSet) {
mDataSet = myDataSet;
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
public ViewGroup v;
public MyViewHolder(ViewGroup v) {
super(v);
}
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return null;
mContext = parent.getContext();
ViewGroup v = (ViewGroup) LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_grow_list, parent, false);
return new MyViewHolder(v);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
PostDetailBean curPost = mDataSet.get(position);
//findView
ImageView posterHead = CommViewHolder.get(holder.itemView, R.id.poster_head);
TextView posterName = CommViewHolder.get(holder.itemView, R.id.tv_poster_name);
TextView postTime = CommViewHolder.get(holder.itemView, R.id.tv_post_time);
TextView postContentText = CommViewHolder.get(holder.itemView, R.id.tv_post_content_text);
NineGridView ngvPicContainer = CommViewHolder.get(holder.itemView, R.id.ngv_pic_container);
//setData
posterName.setText(curPost.getUserName());
Glide.with(mContext).load(curPost.getUserHead()).into(posterHead);
postTime.setText(TimeConvert.getTimeElapse(curPost.getPostTime()));
postContentText.setText(curPost.getPostTextContent());
ArrayList<ImageInfo> imageInfoList = new ArrayList<>();
ArrayList<String> postPicUrls = curPost.getPostPicUrls();
if (postPicUrls != null) {
for (String picUrl : postPicUrls) {
ImageInfo info = new ImageInfo();
info.setThumbnailUrl(picUrl);
info.setBigImageUrl(picUrl);
imageInfoList.add(info);
}
}
ngvPicContainer.setAdapter(new NineGridViewClickAdapter(mContext, imageInfoList));
}
@Override
public int getItemCount() {
return 0;
return mDataSet == null ? 0 : mDataSet.size();
}
public void updateData(String s) {
public void updateData(ArrayList<PostDetailBean> myDataSet) {
mDataSet=myDataSet;
notifyDataSetChanged();
}
}

View File

@ -0,0 +1,73 @@
package com.yuxihan.sdu.ui.grow.adapter;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import com.lzy.ninegrid.ImageInfo;
import com.lzy.ninegrid.NineGridView;
import com.lzy.ninegrid.NineGridViewAdapter;
import com.lzy.ninegrid.preview.ImagePreviewActivity;
import java.io.Serializable;
import java.util.List;
public class NineGridViewClickAdapter extends NineGridViewAdapter {
private int statusHeight;
public NineGridViewClickAdapter(Context context, List<ImageInfo> imageInfo) {
super(context, imageInfo);
statusHeight = getStatusHeight(context);
}
@Override
protected void onImageItemClick(Context context, NineGridView nineGridView, int index,
List<ImageInfo> imageInfo) {
for (int i = 0; i < imageInfo.size(); i++) {
ImageInfo info = imageInfo.get(i);
View imageView;
if (i < nineGridView.getMaxSize()) {
imageView = nineGridView.getChildAt(i);
} else {
//如果图片的数量大于显示的数量则超过部分的返回动画统一退回到最后一个图片的位置
imageView = nineGridView.getChildAt(nineGridView.getMaxSize() - 1);
}
info.imageViewWidth = imageView.getWidth();
info.imageViewHeight = imageView.getHeight();
int[] points = new int[2];
imageView.getLocationInWindow(points);
info.imageViewX = points[0];
info.imageViewY = points[1] - statusHeight;
}
Intent intent = new Intent(context, ImagePreviewActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable(ImagePreviewActivity.IMAGE_INFO, (Serializable) imageInfo);
bundle.putInt(ImagePreviewActivity.CURRENT_ITEM, index);
intent.putExtras(bundle);
context.startActivity(intent);
((Activity) context).overridePendingTransition(0, 0);
}
/**
* 获得状态栏的高度
*/
public int getStatusHeight(Context context) {
int statusHeight = -1;
try {
@SuppressLint("PrivateApi")
Class<?> clazz = Class.forName("com.android.internal.R$dimen");
Object object = clazz.newInstance();
int height =
Integer.parseInt(clazz.getField("status_bar_height").get(object).toString());
statusHeight = context.getResources().getDimensionPixelSize(height);
} catch (Exception e) {
e.printStackTrace();
}
return statusHeight;
}
}

View File

@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.grow.GrowFragment">
<RelativeLayout
@ -38,17 +39,21 @@
android:src="@drawable/ic_add" />
</RelativeLayout>
<ScrollView
<com.lcodecore.tkrefreshlayout.TwinklingRefreshLayout
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintHeight_percent="0.5"
app:layout_constraintTop_toBottomOf="@+id/rl_grow_title">
<!-- app:tr_head_height="100dp"
app:tr_wave_height="180dp"-->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_grow_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
android:layout_height="match_parent"
android:overScrollMode="never" />
</com.lcodecore.tkrefreshlayout.TwinklingRefreshLayout>
</LinearLayout>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include
android:id="@+id/ll_poster_info"
layout="@layout/item_poster_info" />
<TextView
android:id="@+id/tv_post_content_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:paddingBottom="10dp"
android:text="若您只使用上传、下载和复制功能,则可以使用简化版的 SDK
首先在根目录下的 build.gradle 中添加 maven 仓库:"
android:textColor="@color/black"
android:textSize="15sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ll_poster_info" />
<com.lzy.ninegrid.NineGridView
android:id="@+id/ngv_pic_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_post_content_text" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="@color/divider_gray"
android:padding="10dp"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,35 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="63dp"
android:padding="10dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/poster_head"
android:layout_width="45dp"
android:layout_height="45dp"
android:src="@drawable/wel_icon" />
<TextView
android:id="@+id/tv_poster_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_toEndOf="@+id/poster_head"
android:text="四脚吞金兽"
android:textColor="@color/black"
android:textSize="15sp"
/>
<TextView
android:id="@+id/tv_post_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@+id/tv_poster_name"
android:layout_alignBottom="@+id/poster_head"
android:textColor="@color/text_hint"
android:textSize="12sp"
android:text="二小时前" />
</RelativeLayout>

View File

@ -13,4 +13,5 @@
<color name="shape1">#5d8df7</color>
<color name="shape2">#5EB9F8</color>
<color name="shape3">#5CC2F8</color>
<color name="text_hint">#888888</color>
</resources>