Preface
上一篇文章已经实现了列表跟宫格布局的动态切换,这篇文章主要来说通过
The cooperation of CoordinatorLayout and AppbarLayout , and NestedScrollView to achieve ceiling effect. The effect is as follows.
1. What is CoordinatorLayout ?
CoordinatorLayout is a layout container in the Android Support Library, used to coordinate interactions and animation effects between sub-Views. It is designed based on the observer pattern, which can coordinate and control the behavior of sub-Views based on the relationships and events between sub-Views. It allows you to implement various coordinated actions and interactive effects in a complex application interface. It is an important component that supports Material Design and can be used to create various complex layouts and animation effects.
2. What is AppbarLayout ?
AppBarLayout is a layout container in the Android Support Library. It is usually used in conjunction with Toolbar and CollapsingToolbarLayout to achieve a collapsible application bar effect. It provides an easy way to implement app bar scrolling and collapsing effects. It encapsulates a lot of scrolling events internally. CollapsingToolbarLayout is also a container in the Android Support Library for collapsible app bar effects.
3. What is NestedScrollView?
NestedScrollView is a nested scrollable view container in the Android Support Library. It extends from ScrollView and can handle scrolling conflicts between multiple scroll views in the case of nested scrolling.
4. Practice
So it is not difficult to understand that when CoordinatorLayout and AppBarLayout are used together, the ceiling effect can be achieved. The usual layout structure is: CoordinatorLayout as the outermost container contains an AppBarLayout inside, and AppBarLayout contains a scrollable view NestedScrollView inside. Through such a layout structure, when the user scrolls the NestedScrollView, the Toolbar and CollapsingToolbarLayout in AppBarLayout will adjust their display status according to the scrolling position. When scrolling up, AppBarLayout collapses, and Toolbar can be hidden. When scrolling down, AppBarLayout expands, and Toolbar is displayed. In order to achieve the ceiling effect, you need to add a property to CollapsingToolbarLayout in AppBarLayout
app:layout_scrollFlags="scroll|enterAlways|snap"
Among them, "scroll" means that scrolling is supported, "enterAlways" means that when scrolling down, it will always enter the visible state, and "snap" means that when the scroll event ends, the Toolbar will be automatically fully displayed or hidden. , in order to achieve the ceiling effect of NestedScrollView, you can set the property on the parent container of NestedScrollView
app:layout_behavior="@string/appbar_scrolling_view_behavior"
This can associate NestedScrollView with AppBarLayout to achieve coordination when scrolling. Next, let’s implement it step by step
1.activity_news_info.xml
Create a new activity, named NewsInfoActivity, to display the article details page, we主要看到吸顶布局结构
CoordinatorLayout contains AppBarLayout, and AppBarLayout contains CollapsingToolbarLayout, which is a collapsible layout that contains a custom header layout. In addition to CollapsingToolbarLayout, AppBarLayout also contains a custom inside_fixed_bar layout, which is a fixed floating box. Outside of it, NestedScrollView contains a RecyclerView list, so roughly The layout for the ceiling effect is now complete. Very scalable. The layout is as follows,
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="match_parent"
android:orientation="vertical">
<per.goweii.actionbarex.ActionBarEx
android:id="@+id/abc_main_return"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#14a4fb"
app:ab_autoImmersion="false"
app:ab_bottomLineColor="#f3f3f3"
app:ab_bottomLineHeight="0dp"
app:ab_statusBarColor="#00000000"
app:ab_statusBarMode="dark"
app:ab_statusBarVisible="true"
app:ab_titleBarHeight="50dp"
app:ab_titleBarLayout="@layout/top" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="false">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="false"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:statusBarScrim="@android:color/transparent">
<include layout="@layout/header" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
<include layout="@layout/inside_fixed_bar" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>
2.CommentsAdapter
Next is the list data adapter displayed after the ceiling
public class CommentsAdapter extends BaseQuickAdapter<NewsCommentBean.Comments, BaseViewHolder> {
public CommentsAdapter(int layoutResId, @Nullable List<NewsCommentBean.Comments> data) {
super(layoutResId, data);
}
@Override
protected void convert(@NonNull BaseViewHolder helper, NewsCommentBean.Comments item) {
try {
helper.setText(R.id.comment_author_tv, item.getAuthor());
helper.setText(R.id.comment_info_tv, item.getContent());
helper.setText(R.id.comment_link_tv, item.getLikes());
String s = App.dateToStamp(item.getTime());
helper.setText(R.id.comment_time_tv, s);
String avatar = item.getAvatar();
ImageView iconImg = helper.getView(R.id.comment_icon_img);
//Glide设置圆形图片
RequestOptions options = new RequestOptions().circleCropTransform();
Glide.with(mContext).load(avatar).apply(options).into(iconImg);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
3.comments_item_layout.xml
If there is adaptation, you need to have a sub-layout item.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="160dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_horizontal">
<ImageView
android:id="@+id/comment_icon_img"
android:layout_width="60dp"
android:layout_height="60dp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:id="@+id/comment_author_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/dp_10"
android:text="唐吉坷德" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/comment_info_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_10"
android:text="波兰,尼日利亚都木有劲本届杯赛"
android:textStyle="bold" />
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/comment_time_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/dp_10"
android:text="48分钟前" />
<TextView
android:id="@+id/comment_link_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginRight="@dimen/dp_10"
android:layout_toLeftOf="@+id/zan_img"
android:text="50" />
<ImageView
android:id="@+id/zan_img"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:background="@mipmap/zan" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_alignParentBottom="true"
android:layout_marginLeft="5dp"
android:layout_marginTop="5dp"
android:layout_marginRight="5dp"
android:background="@color/light_gray" />
</RelativeLayout>
</LinearLayout>
</LinearLayout>
4.NewsInfoActivity
In the previous article, add a sub-item click event into NewsInfoActivity, and pass in the relevant url, news id and news title. Here you should pay attention to why the click event should be written in a separate method, because I am When running, the sub-item click event becomes invalid after switching between the grid and the list, so after encapsulating it in a method, just call the method again
private void adaperChick() {
adapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
@Override
public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
//新闻ID
String news_id = mList.get(position).getNews_id();
//新闻网址
String url = mList.get(position).getUrl();
//新闻标题
String title = mList.get(position).getTitle();
Intent intent = new Intent(MainActivity.this, NewsInfoActivity.class);
intent.putExtra("url", url);
intent.putExtra("news_id", news_id);
intent.putExtra("title", title);
startActivity(intent);
}
});
}
public class NewsInfoActivity extends BaseActivity {
private String news_id;
private TextView infoTv;
private RecyclerView recyclerView;
private LinearLayoutManager linearLayoutManager;
// private NormalAdapter normalAdapter;
private TextView titleTv;
private String title;
private TextView titleInfoTv;
private ImageView btnImg;
private List<NewsCommentBean.Comments> recentList = new ArrayList<>();
private LinearLayoutManager layoutManager;
private CommentsAdapter adapter;
private TextView numTv;
private LinearLayout backLayoput;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news_info);
Intent intent = getIntent();
news_id = intent.getStringExtra("news_id");
title = intent.getStringExtra("title");
infoTv = findViewById(R.id.info_tv);
initData(news_id);
initView();
initComments(news_id);
titleTv.setText("文章详情");
titleInfoTv.setText(title);
btnImg.setVisibility(View.GONE);
layoutManager = new LinearLayoutManager(NewsInfoActivity.this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setNestedScrollingEnabled(false);
adapter = new CommentsAdapter(R.layout.comments_item_layout, recentList);
recyclerView.setAdapter(adapter);
}
private void initComments(String id) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://news-at.zhihu.com/")
//设置数据解析器
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiUrl apiUrl = retrofit.create(ApiUrl.class);
Call<NewsCommentBean> comment = apiUrl.getComment(id);
comment.enqueue(new Callback<NewsCommentBean>() {
@Override
public void onResponse(Call<NewsCommentBean> call, Response<NewsCommentBean> response) {
NewsCommentBean body = response.body();
Gson gson = new Gson();
String s = gson.toJson(body);
List<NewsCommentBean.Comments> recent = body.getRecent();
if (recent.size() > 0) {
try {
recentList.addAll(recent);
adapter.setNewData(recentList);
runOnUiThread(new Runnable() {
@Override
public void run() {
numTv.setText(recent.size() + "");
}
});
} catch (Exception e) {
String message = e.getMessage();
e.printStackTrace();
}
}
}
@Override
public void onFailure(Call<NewsCommentBean> call, Throwable t) {
}
});
}
private void initView() {
recyclerView = findViewById(R.id.recycler_view);
titleTv = findViewById(R.id.top_tv_function);
titleInfoTv = findViewById(R.id.title_tv);
btnImg = findViewById(R.id.btn_main_menu);
numTv = findViewById(R.id.num_tv);
backLayoput = findViewById(R.id.btn_back_layout);
backLayoput.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
private void initData(String news_id) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://news-at.zhihu.com/")
//设置数据解析器
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiUrl apiUrl = retrofit.create(ApiUrl.class);
Call<NewsInfoBean> newsInfoBean = apiUrl.getNewsInfoBean(news_id);
newsInfoBean.enqueue(new Callback<NewsInfoBean>() {
@Override
public void onResponse(Call<NewsInfoBean> call, Response<NewsInfoBean> response) {
NewsInfoBean body = response.body();
String body1 = body.getBody();
Document doc = Jsoup.parse(body1);
Elements elements = doc.select("div.content"); //获取<div class="content">里的内容
for (Element element : elements) {
String text = element.text(); //获取标签内的文本内容
infoTv.setText(text); //将解析出来的文本内容设置到TextView上
}
}
@Override
public void onFailure(Call<NewsInfoBean> call, Throwable t) {
}
});
}
}
Summarize
A small and very practical function has been completed. The next article will continue to implement the multi-layout effect of RecyclerView. In the future, functions such as local caching of lists will be added. The demo is attached at the end of this series of articles. You may as well like and collect it~
The green mountains will not change, and the green waters will flow forever. Goodbye in the rivers and lakes ~