• Material Design的基本使用方法、Tollbar、菜单等


    一、Toolbar

    1、基本用法

    <androidx.appcompat.widget.Toolbar
                                       android:id="@+id/toolbar"
                                       android:layout_width="match_parent"
                                       android:layout_height="?attr/actionBarSize"
                                       android:background="@color/cardview_dark_background"
                                       android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                                       app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • background设置颜色
    • theme设置主题色

    在这里的用法是仅改变Toolbar的主题为深色但是不影响,文字的颜色。

    //设置导航栏
    setSupportActionBar(toolbar);
    
    //得到这个导航栏
    ActionBar actionBar = getSupportActionBar();
    
    if (actionBar != null) {
        actionBar.setDisplayHomeAsUpEnabled(true);  //打开homeAsUp按钮
        actionBar.setHomeAsUpIndicator(R.drawable.apple);   //为这个按钮设置图片
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    setDisplayHomeAsUpEnabled(true)启用了返回箭头按钮。在onOptionsItemSelected方法中,我们处理了箭头按钮的点击事件,通常使用onBackPressed()方法来执行返回操作。

    image-20230808110426220

    • popupTheme设置菜单颜色

    当我们使用theme设置主题颜色以后也会导致Toolbar上的菜单颜色发生变化,这时为了不影响菜单颜色我们又重新设置菜单颜色为浅色。

    app命名是为了兼容低版本

    2、Toolbar中的菜单

    <menu xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:app="http://schemas.android.com/apk/res-auto">
        <item
              android:id="@+id/apple"
              android:icon="@drawable/apple"
              android:title="apple"
              app:showAsAction="always"/>
        <item
              android:id="@+id/onion"
              android:icon="@drawable/onion"
              android:title="onion"
              app:showAsAction="ifRoom"/>
        <item
              android:id="@+id/tomato"
              android:icon="@drawable/tomato"
              android:title="tomato"
              app:showAsAction="never"/>
    
    menu>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    showAsAction指定按钮的显示位置,常用参数的意义

    • always:总是显示在Toolbar中
    • ifRoom:如果有空间就显示在Toolbar,没有空间就显示在菜单中
    • never:永远显示在菜单中

    在MainActivity中使用**setSupportActionBar()**设置导航栏

    二、滑动菜单

    1、DrawerLayout——抽屉布局

    
    <androidx.drawerlayout.widget.DrawerLayout 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:id="@+id/drawerlayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="@color/cardview_dark_background"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
    
        FrameLayout>
    
        <com.google.android.material.navigation.NavigationView
            android:id="@+id/navigation_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            app:menu="@menu/toolbar" /> 
    
    androidx.drawerlayout.widget.DrawerLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    image-20230808161538681

    通过按钮打开这个DrawerLayout

    public class MainActivity extends AppCompatActivity {
    
    
        private DrawerLayout mDrawerlayout;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Toolbar toolbar = findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);
            mDrawerlayout = (DrawerLayout) findViewById(R.id.drawerlayout);
            ActionBar actionBar = getSupportActionBar();
    
            if(actionBar != null){
                actionBar.setDisplayHomeAsUpEnabled(true);  //打开homeAsUp按钮
                actionBar.setHomeAsUpIndicator(R.drawable.apple);   //为这个按钮设置图片
            }
    
        }
    
        @Override
        public boolean onOptionsItemSelected(@NonNull MenuItem item) {
            if(item.getItemId() == android.R.id.home){
                mDrawerlayout.openDrawer(GravityCompat.START);  //点击按钮后打开这个滑动窗口
            }
            return true;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    android.R.id.home:是HomeAsUp按钮的默认id

    GravityCompat.START:用于启动滑动窗口的标识符

    2、NavigationView——导航视图

    2.1 将图片切割为圆形的工具

    地址:https://github.com/hdodenhof/CircleImageView.git

    dependencies {
        ...
            implementation 'de.hdodenhof:circleimageview:3.1.0'
    }
    
    • 1
    • 2
    • 3
    • 4

    2.2 使用方法

    接下来编写一个新的显示界面nav_herder.xml

    
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="10dp">
    
        <de.hdodenhof.circleimageview.CircleImageView
            android:id="@+id/iconimg"
            android:layout_width="70dp"
            android:layout_height="70dp"
            android:src="@drawable/img"
            android:layout_centerInParent="true"/>
    
        <TextView
            android:id="@+id/mailtext"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="204dp"
            android:text="s1793026645@126.com"
            android:textSize="15sp" />
    
        <TextView
            android:id="@+id/userText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/iconimg"
            android:layout_marginTop="135dp"
            android:gravity="end"
            android:text="lukecc0"
            android:textSize="15sp" />
    
    RelativeLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    activity_main中将这个页面和刚才的menu菜单一起加载到NavigationView中显示。

    <com.google.android.material.navigation.NavigationView
            android:id="@+id/navigation_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            app:menu="@menu/toolbar"
            app:headerLayout="@layout/nav_header"/>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    打开滑动窗口后显示效果如下:

    image-20230808173707303

    注意我们发现apple这个菜单选项是被选中的状态,是因为我们在MainActivity中使用这句代码设置的。nacview

    NavigationView nacview = (NavigationView) findViewById(R.id.navigation_view);
    
    nacview.setCheckedItem(R.id.apple); //设置为选中状态
    
    • 1
    • 2
    • 3

    2.3 设置NavigationView的点击事件

    nacview.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            if (item.getItemId() == R.id.tomato){
                Toast.makeText(MainActivity.this, "666", Toast.LENGTH_SHORT).show();
            }
            if (item.getItemId() == R.id.apple){
                //关闭滑动窗口
                mDrawerlayout.close();
            }
            return true;
        }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    三、悬浮按钮与可交互提示、监听布局

    1、FloatingActionButton——悬浮按钮

    <com.google.android.material.floatingactionbutton.FloatingActionButton
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_gravity="bottom|end"
           android:layout_margin="30dp"
           android:src="@drawable/apple"
           android:elevation="8dp"/>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    image-20230809101235080

    这里使用app:elevation属性来给FloatingActionButton指定一个高度值,高度值越大,投影范围也越大,但是投影效果越淡,高度值越小,投影范围也越小,但是投影效果越浓。

    2、Snackbar——可交互提示

    floatingActionButton = (FloatingActionButton) findViewById(R.id.FloatingButton);
    floatingActionButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "这是一条通知", Snackbar.LENGTH_SHORT)
                .setAction("Undo", new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Toast.makeText(MainActivity.this, "你好", Toast.LENGTH_SHORT).show();
                    }
                }).show();
        }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里我们调用了Snackbar的make方法创建一个Snackbar对象,第一个参数是View,只要是任意一个View都可以他可以自己寻找到最外层布局。

    然后使用setAction可以为提示设置点击事件实现交互的目的。

    image-20230809102925182

    3、Coordinatorlayout——监听布局

    相当于一个增强版的Framelayout,可以监听页面的子控件,然后做出合理响应。

    
    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/cardview_dark_background"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
    
        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/FloatingButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="30dp"
            android:src="@drawable/apple"
            android:elevation="8dp"/>
    
    androidx.coordinatorlayout.widget.CoordinatorLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    可以看出它自动使得FloatingActionButton向上偏移了。

    四、卡片式布局

    1、MaterialCardView——实现卡片效果

    实际上MaterialCardView也属于FrameLayout,只是额外提供了阴影和圆角等效果

    制作一个RecyclerView的子布局fruit_item.xml,使用MaterialCardView这个布局方式。

    
    <com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_margin="5dp"
        app:cardCornerRadius="4dp">
    
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
            <ImageView
                android:id="@+id/FruitImage"
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:scaleType="centerCrop"/>
    
            <TextView
                android:id="@+id/FruitText"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:layout_margin="5dp"
                android:textSize="16sp"/>
    
        LinearLayout>
    
    com.google.android.material.card.MaterialCardView>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    接下来使用RecyclerView需要制作一个adapter

    public class Fruit {
        private String name;
        private int imageId;
    
        public Fruit(String name, int imageId) {
            this.name = name;
            this.imageId = imageId;
        }
    
        public String getName() {
            return name;
        }
    
        public int getImageId() {
            return imageId;
        }
    }
    public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
    
        private Context mcontext;
        private List<Fruit> mfruit;
    
        public FruitAdapter(List<Fruit> mfruit) {
            this.mfruit = mfruit;
        }
    
        class ViewHolder extends RecyclerView.ViewHolder {
    
            FruitItemBinding binding;
    
            public ViewHolder(@NonNull View itemView) {
                super(itemView);
                binding = FruitItemBinding.bind(itemView);
            }
        }
    
        @NonNull
        @Override
        public FruitAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    
            //获取主视图的上下文
            if (mcontext == null){
                mcontext = parent.getContext();
            }
    
            View view = LayoutInflater.from(mcontext).inflate(R.layout.fruit_item, parent, false);
            ViewHolder holder = new ViewHolder(view);
            return holder;
        }
    
        @Override
        public void onBindViewHolder(@NonNull FruitAdapter.ViewHolder holder, int position) {
            Fruit fruit = mfruit.get(position);
            holder.binding.FruitText.setText(fruit.getName());
            Glide.with(mcontext).load(fruit.getImageId()).into(holder.binding.FruitImage);
        }
    
        @Override
        public int getItemCount() {
            return mfruit.size();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    并且在activity_main布局中设置recyclerView控件

    image-20230809115408116

    最后在MainActivity中启动即可

            initFruits();
            recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
            GridLayoutManager gridLayoutManager = new GridLayoutManager(this,2);
            recyclerView.setLayoutManager(gridLayoutManager);
    
            FruitAdapter adapter = new FruitAdapter(fruitList);
            recyclerView.setAdapter(adapter);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    GridLayoutManager有两个参数,第二个参数意义是列数。

    注意使用这种MaterialCardView需要保证应用主题必须是 Theme.MaterialComponents 或其后代主题。

    显示效果如下:

    image-20230809115909687

    在这里发现我们的Toolbar不见了,仔细观察发现被recyclerView遮挡了。这时候就需要另外一个工具——AppBarLayout

    2、AppBarLayout

    用于和recyclerView进行交互作用,

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        
       <androidx.appcompat.widget.Toolbar
                 android:id="@+id/toolbar"
                 android:layout_width="match_parent"
                 android:layout_height="?attr/actionBarSize"
                 android:background="@color/cardview_dark_background"
                 android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                 app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                 app:layout_scrollFlags="scroll|enterAlways|snap"/>com.google.android.material.appbar.AppBarLayout>
    
        <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    首先我们定义了一个AppBarLayout将Toolbar放入AppBarLayout中。然后在RecyclerView中使用app:layout_behavior属性指定了一个布局行为,

    2.1 app:layout_behavior

    appbar_scrolling_view_behavior这个字符串也是Material库提供的。

    通过app:layout_behavior这个属性可以解决遮挡问题。可以看出它将recyclerView向下挤压了。

    image-20230809153357386

    app:layout_behavior="@string/appbar_scrolling_view_behavior" 是用于指定一个视图在滚动时与 AppBarLayout 一起协调工作的属性。这个属性通常用于将滚动行为与 CoordinatorLayoutAppBarLayout 一起使用,以实现复杂的滚动效果

    2.2 app:layout_scrollFlags

    还记得我们在父布局使用的CoordinatorLayout吗?他可以用于监听控件变化。

    可以使用 app:layout_scrollFlags 属性来定义 AppBarLayout的滚动行为。

    下面是一些常见的 layout_scrollFlags 标志:

    • scroll: 指示该视图可以在滚动时折叠,即它会随着滚动事件的发生而逐渐变小。
    • enterAlways: 指示该视图在向下滚动时,会立即进入可见状态。通常与 scroll 一起使用。
    • snap: 指示该视图会在滚动结束时对齐到最近的折叠状态,以产生“捕捉”效果。

    五、下拉刷新效果——SwipRefreshLayout

    首先导入依赖:

    依赖地址: 谷歌API参考文档

    dependencies {
        implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
    }
    
    • 1
    • 2
    • 3

    我们接下来使用SwipRefreshLayout将RecyclerView包住

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/swiprefresh"
        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/recyclerView"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           app:layout_behavior="@string/appbar_scrolling_view_behavior" />
    androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    然后我们还需设置具体的刷新逻辑才可以实现刷新功能。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        swipeRefreshLayout = (SwipeRefreshLayout) findViewById((int) R.id.swiprefresh);
    
        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                //调用写好的刷新逻辑
                refreshFruits();
            }
        });
    }
    
    //具体刷新逻辑
    private void refreshFruits(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //因为本地刷新很快,这样便于我们观察到刷新效果
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
    
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        initFruits();
                        //通知RecyclerView刷新
                        adapter.notifyDataSetChanged();
    
                        //表示刷新事件结束,隐藏进度条
                        swipeRefreshLayout.setRefreshing(false);
                    }
                });
            }
        }).start();
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    最终显示效果如下:

    image-20230809163543864

  • 相关阅读:
    基础语法——组合与继承
    php组件漏洞
    安科瑞环保用电平台助力绘就环保产业“双碳”路线图
    优思学院|质量4.0与质量管理的未来
    坚持每天做一件事情的意义
    信贷风控拒绝客户的捞回策略详解
    Vue扩展组件mixins,extends,composition api,slots
    Android学习笔记 83. 卡片和颜色
    Java 获取一个时间范围内的最后一天或者第一天
    Handler消息机制,postDelayed会造成线程阻塞吗?对内存有什么影响?
  • 原文地址:https://blog.csdn.net/m0_72983118/article/details/132805570