Android RecyclerView ceiling effect

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 ~

Guess you like

Origin blog.csdn.net/X_sunmmer/article/details/132561672
Recommended