项目地址
这一篇我们主要完成首页布局搭建,我们先看下要实现的效果
首页基本上就是一个列表,有下拉刷新和上拉加载功能,所以我们主要就是完成每个Item
的布局即可
我们首先分析下这个布局
这里我们把它分解为5部分,然后我们分别完成每部分的布局,最后组合在一起
我们先通过接口返回值拿一下Item
数据
{
"id": 428,
"itemId": 1578976510452,
"itemType": 2,
"createTime": 1578977844500,
"duration": 8,
"feeds_text": "2020他来了,就在眼前了",
"authorId": 1578919786,
"activityIcon": null,
"activityText": "2020新年快乐",
"width": 960,
"height": 540,
"url": "https://pipijoke.oss-cn-hangzhou.aliyuncs.com/New%20Year%20-%2029212-video.mp4",
"cover": "https://pipijoke.oss-cn-hangzhou.aliyuncs.com/2020%E5%B0%81%E9%9D%A2%E5%9B%BE.png",
"author": {
"id": 1250,
"userId": 1578919786,
"name": "、蓅哖╰伊人为谁笑",
"avatar": "http://qzapp.qlogo.cn/qzapp/101794421/FE41683AD4ECF91B7736CA9DB8104A5C/100",
"description": "这是一只神秘的jetpack",
"likeCount": 8,
"topCommentCount": 0,
"followCount": 3,
"followerCount": 62,
"qqOpenId": "FE41683AD4ECF91B7736CA9DB8104A5C",
"expires_time": 1586695789903,
"score": 0,
"historyCount": 5189,
"commentCount": 42,
"favoriteCount": 3,
"feedCount": 0,
"hasFollow": false
},
"topComment": {
"id": 1126,
"itemId": 1578976510452,
"commentId": 1579007787804000,
"userId": 1578919786,
"commentType": 1,
"createTime": 1579007787804,
"commentCount": 0,
"likeCount": 1001,
"commentText": "2020他来了,就在眼前了~Happy New Year",
"imageUrl": "",
"videoUrl": "",
"width": 0,
"height": 0,
"hasLiked": false,
"author": {
"id": 1250,
"userId": 1578919786,
"name": "、蓅哖╰伊人为谁笑",
"avatar": "http://qzapp.qlogo.cn/qzapp/101794421/FE41683AD4ECF91B7736CA9DB8104A5C/100",
"description": "这是一只神秘的jetpack",
"likeCount": 8,
"topCommentCount": 0,
"followCount": 3,
"followerCount": 62,
"qqOpenId": "FE41683AD4ECF91B7736CA9DB8104A5C",
"expires_time": 1586695789903,
"score": 0,
"historyCount": 5189,
"commentCount": 42,
"favoriteCount": 3,
"feedCount": 0,
"hasFollow": false
},
"ugc": {
"likeCount": 125,
"shareCount": 10,
"commentCount": 10,
"hasFavorite": false,
"hasLiked": false,
"hasdiss": false,
"hasDissed": false
}
},
"ugc": {
"likeCount": 1458,
"shareCount": 29,
"commentCount": 532,
"hasFavorite": false,
"hasLiked": false,
"hasdiss": false,
"hasDissed": false
}
}
我们通过GsonFormat
插件生成几个ItemBean
/**
* 作者信息
*/
public class User implements Serializable {
/**
* id : 962
* userId : 3223400206308231
* name : 二师弟请随我来
* avatar : https://p3-dy.byteimg.com/img/p1056/8c50025c85244140910a513345ae7358~200x200.webp
* description :
* likeCount : 0
* topCommentCount : 0
* followCount : 0
* followerCount : 0
* qqOpenId : null
* expires_time : 0
* score : 0
* historyCount : 0
* commentCount : 0
* favoriteCount : 0
* feedCount : 0
* hasFollow : false
*/
public int id;
public long userId;
public String name;
public String avatar;
public String description;
public int likeCount;
public int topCommentCount;
public int followCount;
public int followerCount;
public String qqOpenId;
public long expires_time;
public int score;
public int historyCount;
public int commentCount;
public int favoriteCount;
public int feedCount;
public boolean hasFollow;
}
/**
* 评论
*/
public class Comment implements Serializable {
/**
* id : 784
* itemId : 6739143063064549000
* commentId : 6739212214408380000
* userId : 65200808093
* commentType : 1
* createTime : 1569095152
* commentCount : 4454
* likeCount : 152
* commentText : 看见没。比甜蜜暴击好看一万倍!
* imageUrl : null
* videoUrl : null
* width : 0
* height : 0
* hasLiked : false
* author : {"id":978,"userId":65200808093,"name":"带鱼裹上面包糠","avatar":"https://sf1-nhcdn-tos.pstatp.com/obj/tos-cn-i-0000/9041325b8fd44dd09fd41d5f2bd379bd","description":null,"likeCount":0,"topCommentCount":0,"followCount":0,"followerCount":0,"qqOpenId":null,"expires_time":0,"score":0,"historyCount":0,"commentCount":0,"favoriteCount":0,"feedCount":0,"hasFollow":false}
* ugc : {"likeCount":153,"shareCount":0,"commentCount":4454,"hasFavorite":false,"hasLiked":true}
*/
public int id;
public long itemId;
public long commentId;
public long userId;
public int commentType;
public long createTime;
public int commentCount;
public int likeCount;
public String commentText;
public String imageUrl;
public String videoUrl;
public int width;
public int height;
public boolean hasLiked;
public User author;
public Ugc ugc;
}
/**
* 点赞,分享
*/
public class Ugc implements Serializable {
/**
* likeCount : 153
* shareCount : 0
* commentCount : 4454
* hasFavorite : false
* hasLiked : true
* hasdiss:false
*/
public int likeCount;
public int shareCount;
public int commentCount;
public boolean hasFavorite;
public boolean hasdiss;
}
/**
* 帖子
*/
public class Feed implements Serializable {
/**
* id : 364
* itemId : 6739143063064549000
* itemType : 2
* createTime : 1569079017
* duration : 299.435
* feeds_text : 当中国地图出来那一幕,我眼泪都出来了!
* 太震撼了!
* authorId : 3223400206308231
* activityIcon : null
* activityText : null
* width : 640
* height : 368
* url : https://pipijoke.oss-cn-hangzhou.aliyuncs.com/6739143063064549643.mp4
* cover : https://p3-dy.byteimg.com/img/mosaic-legacy/2d676000e36289f35f70c~640x368_q80.webp
*/
public int id;
public long itemId;
public int itemType;
public long createTime;
public double duration;
public String feeds_text;
public long authorId;
public String activityIcon;
public String activityText;
public int width;
public int height;
public String url;
public String cover;
public User author;
public Comment topComment;
public Ugc ugc;
}
有了JavaBean
,接下来我们就编写xml
文件,注意,我们将使用DataBinding
使用DataBinding
我们需要先在gradle
中配置下,然后就可以使用了
我们先编写第一部分
这个布局很简单.左面是一个ImageView
,右边是一个TextView
,我们来写下
<layout 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">
<data>
<variable
name="user"
type="User" />
<import type="com.hfs.jokevideo.model.User" />
data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dp_40"
android:layout_marginBottom="@dimen/dp_10"
android:orientation="horizontal"
android:paddingLeft="@dimen/dp_16"
android:paddingRight="@dimen/dp_16">
<com.hfs.libcommon.view.PPImageView
android:id="@+id/avatar"
android:layout_width="@dimen/dp_40"
android:layout_height="@dimen/dp_40"
app:image_url="@{user.avatar}"
app:isCircle="@{true}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/author_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_10"
android:gravity="center"
android:text="@{user.name}"
android:textColor="@color/color_333"
android:textSize="@dimen/sp_14"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/create_time"
app:layout_constraintLeft_toRightOf="@+id/avatar"
app:layout_constraintTop_toTopOf="parent"
tools:text="use_name" />
<TextView
android:id="@+id/create_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_10"
android:textColor="@color/color_999"
android:textSize="@dimen/sp_12"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/avatar"
app:layout_constraintTop_toBottomOf="@+id/author_name"
tools:text="1小时前" />
<ImageView
android:id="@+id/feed_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/dimen_10"
android:src="@drawable/icon_item_cell_delete"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
androidx.constraintlayout.widget.ConstraintLayout>
layout>
DataBinding
使用,xml
用layout
包裹,然后下面是data
标签,import
表示我们要在xml
中引入的类,variable
是我们在xml
中声明的变量,下面我们就可以使用这个变量了,例如给TextView
赋值android:text="@{user.name}"
,这个user
及时是null
,这里也不会报错的.这里要注意的是ImageView
我们不能直接通过给它设置url
来加载的,所以这里我们通过自定义View的方式实现
public class PPImageView extends AppCompatImageView {
public PPImageView(Context context) {
super(context);
}
public PPImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public PPImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
ViewHelper.setViewOutline(this, attrs, defStyleAttr, 0);
}
public void setImageUrl(String imageUrl) {
setImageUrl(this, imageUrl, false);
}
@BindingAdapter(value = {"image_url", "isCircle"})
public static void setImageUrl(PPImageView view, String imageUrl, boolean isCircle) {
view.setImageUrl(view, imageUrl, isCircle, 0);
}
@BindingAdapter(value = {"image_url", "isCircle", "radius"}, requireAll = false)
public static void setImageUrl(PPImageView view, String imageUrl, boolean isCircle, int radius) {
RequestBuilder<Drawable> builder = Glide.with(view).load(imageUrl);
if (isCircle) {
builder.transform(new CircleCrop());
} else if (radius > 0) {
builder.transform(new RoundedCornersTransformation(PixUtils.dp2px(radius), 0));
}
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
if (layoutParams != null && layoutParams.width > 0 && layoutParams.height > 0) {
builder.override(layoutParams.width, layoutParams.height);
}
builder.into(view);
}
public void bindData(int widthPx, int heightPx, int marginLeft, String imageUrl) {
bindData(widthPx, heightPx, marginLeft, PixUtils.getScreenWidth(), PixUtils.getScreenWidth(), imageUrl);
}
public void bindData(int widthPx, int heightPx, final int marginLeft, final int maxWidth, final int maxHeight, String imageUrl) {
if (TextUtils.isEmpty(imageUrl)) {
setVisibility(GONE);
return;
} else {
setVisibility(VISIBLE);
}
if (widthPx <= 0 || heightPx <= 0) {
Glide.with(this).load(imageUrl).into(new SimpleTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
int height = resource.getIntrinsicHeight();
int width = resource.getIntrinsicWidth();
setSize(width, height, marginLeft, maxWidth, maxHeight);
setImageDrawable(resource);
}
});
return;
}
setSize(widthPx, heightPx, marginLeft, maxWidth, maxHeight);
setImageUrl(this, imageUrl, false);
}
private void setSize(int width, int height, int marginLeft, int maxWidth, int maxHeight) {
int finalWidth, finalHeight;
if (width > height) {
finalWidth = maxWidth;
finalHeight = (int) (height / (width * 1.0f / finalWidth));
} else {
finalHeight = maxHeight;
finalWidth = (int) (width / (height * 1.0f / finalHeight));
}
ViewGroup.LayoutParams params = getLayoutParams();
params.width = finalWidth;
params.height = finalHeight;
if (params instanceof FrameLayout.LayoutParams) {
((FrameLayout.LayoutParams) params).leftMargin = height > width ? PixUtils.dp2px(marginLeft) : 0;
} else if (params instanceof LinearLayout.LayoutParams) {
((LinearLayout.LayoutParams) params).leftMargin = height > width ? PixUtils.dp2px(marginLeft) : 0;
}
setLayoutParams(params);
}
@BindingAdapter(value = {"blur_url", "radius"})
public static void setBlurImageUrl(final ImageView imageView, String blurUrl, int radius) {
Glide.with(imageView).load(blurUrl).override(radius)
.transform(new BlurTransformation())
.dontAnimate()
.into(new SimpleTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
imageView.setBackground(resource);
}
});
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="feedText"
type="java.lang.String" />
<variable
name="lines"
type="java.lang.Integer" />
<import type="android.text.TextUtils"/>
<import type="android.view.View"/>
</data>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_16"
android:layout_marginRight="@dimen/dp_16"
android:layout_marginBottom="@dimen/dp_10"
android:ellipsize="end"
android:maxLines="@{lines}"
android:orientation="vertical"
android:text="@{feedText}"
android:textColor="@color/color_2f2"
android:textSize="@dimen/sp_16"
android:visibility="@{TextUtils.isEmpty(feedText)?View.GONE:View.VISIBLE}"
tools:text="神秘的jetpack神秘的jetpack">
</TextView>
</layout>
接下来是一个图片或者是一个视频,比较复杂我们放在最后,我们先写下面的tag和ugc
我们先看这个标签
使用文本或者一个按钮即可
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="tagText"
type="java.lang.String" />
<import type="android.text.TextUtils"/>
<import type="android.view.View"/>
</data>
<com.google.android.material.button.MaterialButton
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="8dp"
android:layout_marginBottom="10dp"
android:gravity="center_vertical"
android:includeFontPadding="false"
android:orientation="vertical"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:stateListAnimator="@null"
android:text="@{tagText}"
android:textColor="@color/color_2f2"
android:textSize="@dimen/sp_10"
android:visibility="@{TextUtils.isEmpty(tagText)?View.GONE:View.VISIBLE}"
app:backgroundTint="@color/color_gray"
app:cornerRadius="10dp"
app:icon="@drawable/icon_tag"
app:iconGravity="textStart"
app:iconPadding="@dimen/dp_4"
app:iconSize="@dimen/dp_12"
app:iconTintMode="multiply"
tools:text="神秘的jetpack">
</com.google.android.material.button.MaterialButton>
</layout>
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="comment"
type="com.hfs.jokevideo.model.Comment" />
<import type="android.text.TextUtils"/>
<import type="android.view.View"/>
<import type="com.hfs.jokevideo.utils.StringConvert"/>
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_60"
android:layout_gravity="right|top"
android:layout_marginEnd="@dimen/dp_10"
android:src="@drawable/icon_god_comment2" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dp_20"
android:layout_marginTop="@dimen/dp_10"
android:orientation="horizontal">
<com.hfs.libcommon.view.PPImageView
android:id="@+id/avatar"
android:layout_width="@dimen/dp_20"
android:layout_height="@dimen/dp_20"
app:image_url="@{comment.author.avatar}"
app:isCircle="@{true}"
tools:src="@mipmap/ic_launcher_round">
</com.hfs.libcommon.view.PPImageView>
<TextView
android:id="@+id/user_name"
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_20"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/dp_10"
android:gravity="center_vertical"
android:text="@{comment.author.name}"
android:textColor="@color/color_3d3"
android:textSize="@dimen/sp_12"
tools:text="神秘的jetpack"/>
<Space
android:layout_width="0dp"
android:layout_height="1px"
android:layout_weight="1"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@{comment.ugc.likeCount>0?StringConvert.convertFeedUgc(comment.ugc.likeCount):null}"
android:textColor="@{comment.ugc.hasLiked?@color/color_theme:@color/color_3d3}"
android:textSize="@dimen/sp_12"
tools:text="1000"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_2"
android:src="@{comment.ugc.hasLiked?@drawable/icon_cell_liked:@drawable/icon_cell_like}"
tools:src="@drawable/icon_cell_liked">
</ImageView>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_10"
android:layout_marginRight="@dimen/dp_36"
android:ellipsize="end"
android:maxLines="2"
android:text="@{comment.commentText}"
android:textColor="@color/color_3d3"
android:textSize="@dimen/sp_14"
android:visibility="@{TextUtils.isEmpty(comment.commentText)?View.GONE:View.VISIBLE}"
tools:text="神秘的JetPack神秘的JetPack神秘的JetPack"/>
<FrameLayout
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginTop="@dimen/dp_10"
android:visibility="@{TextUtils.isEmpty(comment.imageUrl)?View.GONE:View.VISIBLE}">
<com.hfs.libcommon.view.PPImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@color/color_theme_10"
android:visibility="@{TextUtils.isEmpty(comment.imageUrl)?View.GONE:View.VISIBLE}"
app:image_url="@{comment.imageUrl}"
app:isCircle="@{false}"
tools:src="@mipmap/ic_launcher"/>
<ImageView
android:layout_width="@dimen/dp_20"
android:layout_height="@dimen/dp_20"
android:layout_gravity="center"
android:src="@drawable/icon_video_play"
android:visibility="@{TextUtils.isEmpty(comment.videoUrl)?View.GONE:View.VISIBLE}"/>
</FrameLayout>
</LinearLayout>
</FrameLayout>
</layout>
接着就是点赞啥的布局
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="feed"
type="com.hfs.jokevideo.model.Feed" />
<variable
name="lifeCycleOwner"
type="androidx.lifecycle.LifecycleOwner" />
<import type="com.hfs.jokevideo.model.Feed" />
<import type="com.hfs.jokevideo.utils.StringConvert" />
<import type="android.content.Context" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dp_45"
android:orientation="horizontal"
android:paddingLeft="@dimen/dp_16"
android:paddingRight="@dimen/dp_16">
<com.google.android.material.button.MaterialButton
android:id="@+id/like"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="@{feed.ugc.likeCount>0?StringConvert.convertFeedUgc(feed.ugc.likeCount):@string/like}"
android:textColor="@{feed.ugc.hasLiked?@color/color_theme:@color/color_3d3}"
android:textSize="@dimen/sp_14"
app:backgroundTint="@color/color_white"
app:cornerRadius="0dp"
app:icon="@{feed.ugc.hasLiked?@drawable/icon_cell_liked:@drawable/icon_cell_like}"
app:iconGravity="textStart"
app:iconPadding="4dp"
app:iconTint="@{feed.ugc.hasLiked?@color/color_theme:@color/color_3d3}"
tools:icon="@drawable/icon_cell_like"
tools:iconTint="@color/color_3d3"
tools:text="1000"
tools:textColor="@color/color_3d3" />
<com.google.android.material.button.MaterialButton
android:id="@+id/diss"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="踩"
android:textColor="@{feed.ugc.hasdiss?@color/color_theme:@color/color_3d3}"
android:textSize="@dimen/sp_14"
app:backgroundTint="@color/color_white"
app:cornerRadius="0dp"
app:icon="@{feed.ugc.hasdiss?@drawable/icon_cell_dissed:@drawable/icon_cell_diss}"
app:iconGravity="textStart"
app:iconPadding="4dp"
app:iconTint="@{feed.ugc.hasdiss?@color/color_theme:@color/color_3d3}"
tools:icon="@drawable/icon_cell_diss"
tools:iconTint="@color/color_3d3"
tools:text="1000"
tools:textColor="@color/color_3d3" />
<com.google.android.material.button.MaterialButton
android:id="@+id/comment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="@{feed.ugc.commentCount>0?StringConvert.convertFeedUgc(feed.ugc.commentCount):@string/feed_comment}"
android:textColor="@color/color_3d3"
android:textSize="@dimen/sp_14"
app:backgroundTint="@color/color_white"
app:cornerRadius="0dp"
app:icon="@drawable/icon_cell_comment"
app:iconGravity="textStart"
app:iconPadding="4dp"
app:iconTint="@color/color_3d3"
tools:icon="@drawable/icon_cell_comment"
tools:iconTint="@color/color_3d3"
tools:text="1000"
tools:textColor="@color/color_3d3" />
<com.google.android.material.button.MaterialButton
android:id="@+id/share"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="@{feed.ugc.shareCount>0?StringConvert.convertFeedUgc(feed.ugc.shareCount):@string/share}"
android:textColor="@color/color_3d3"
android:textSize="@dimen/sp_14"
app:backgroundTint="@color/color_white"
app:cornerRadius="0dp"
app:icon="@drawable/icon_cell_share"
app:iconGravity="textStart"
app:iconPadding="4dp"
app:iconTint="@color/color_3d3"
tools:icon="@drawable/icon_cell_share"
tools:iconTint="@color/color_3d3"
tools:text="1000"
tools:textColor="@color/color_3d3" />
</LinearLayout>
</layout>
OK,接下里就是中间的图片或者视频区域了,图片的话很简单,直接使用我们上面自定义的ImageView
即可,主要就是这个VideoView
,这个我们下一篇实现