【第一行代码】MaterialTest

简介

介绍了 toolbar 滑动菜单栏 卡片式布局 下拉刷新 可折叠toolbar

添加了日志打印管理类

Material Design

优化 Android UI 界面风格,Android5.0 之后内置的应用都使用该风格设计,目的是想解决 Android 平台界面风格不统一的问题。

Toolbar

继承了 ActionBar 的所有功能,并且灵活性很高。

修改 Application 的 theme 属性,将其设置为 NoActionBar

在 AndroidManifest.xml 文件中,找到 application 的 android:theme 设置

<application
    //其他属性
    android:theme="@style/AppTheme">

这个属性的具体定义在 res/values/styles.xml, 修改这个文件为:

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
    <style name="FruitActivityTheme" parent="AppTheme"/>
</resources>

用 Toolbar 代替 ActionBar

在界面布局文件中添加 Toolbar 控件

<androidx.appcompat.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="?attr/colorPrimary"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    app:layout_scrollFlags="scroll|enterAlways|snap">

</androidx.appcompat.widget.Toolbar>

在代码中获取实例

Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

其中 Toolbar 的文字内容会从当前 Activity 的 android:label 属性获取,如果没有指定,则会从该 application 的 android:label 属性获取。

为 Toolbar 添加菜单功能

在 res 目录下面新建 menu 目录,并且新建一个 toolbar.xml 文件

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app = "http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/backup"
        android:icon="@drawable/back"
        android:title="Backup"
        app:showAsAction="always"/>

    <item
        android:id="@+id/delete"
        android:icon="@drawable/delete"
        android:title="Delete"
        app:showAsAction="ifRoom"/>

    <item
        android:id="@+id/settings"
        android:icon="@drawable/settings"
        android:title="Settings"
        app:showAsAction="never"/>

</menu>
<!--always 图标总是显示 ifRoom 屏幕空间足够的情况下显示 never 永远只显示在菜单中-->

在代码中获取实例并且实现点击监听功能

public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.toolbar, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
    switch (item.getItemId()) {
        case R.id.backup:
            Toast.makeText(this, "back", Toast.LENGTH_SHORT).show();
            break;
        case R.id.delete:
            Toast.makeText(this, "delete", Toast.LENGTH_SHORT).show();
            break;
        case R.id.settings:
            Toast.makeText(this, "settings", Toast.LENGTH_SHORT).show();
            break;
            default:break;
    }
    return true;
}

滑动菜单

借助 DrawerLayout 控件可以简单方便的去实现滑动菜单

修改布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!--忽略其他的一些控件-->

    <!--下面这个是用来显示滑动的菜单, layout_gravity 表示滑动菜单在屏幕的哪一边-->
    <!--这个滑动菜单包含 menu 的菜单项和 headerLayout 的头部布局-->
    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:menu="@menu/nav_menu"
        app:headerLayout="@layout/nav_header">

    </com.google.android.material.navigation.NavigationView>
  
</androidx.drawerlayout.widget.DrawerLayout>

添加 nav_menu.xml 布局作为滑动菜单的菜单项,在 menu 目录下添加

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/nav_call"
            android:icon="@drawable/phone"
            android:title="Call"/>
        <item
            android:id="@+id/nav_friends"
            android:icon="@drawable/friends"
            android:title="Friends"/>
        <item
            android:id="@+id/nav_location"
            android:icon="@drawable/location"
            android:title="Location"/>
        <item
            android:id="@+id/nav_mail"
            android:icon="@drawable/mail"
            android:title="Mail"/>
        <item
            android:id="@+id/nav_task"
            android:icon="@drawable/task"
            android:title="Tasks"/>
    </group>

</menu>

添加 nav_header.xml 布局作为滑动菜单的头部项,在 layout 目录下添加

需要注意的是这里用到了 CircleImageView 控件,也就是圆形头像框,这个需要自己添加依赖

在 build.gradle 添加 implementation 'de.hdodenhof:circleimageview:2.1.0'

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="180dp"
    android:padding="10dp"
    android:background="?attr/colorPrimary">

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/icon_image"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:src="@drawable/avatar"
        android:layout_centerInParent="true"/>

    <TextView
        android:id="@+id/username"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="James@gmail.com"
        android:textColor="#fff"
        android:textSize="14sp"/>

    <TextView
        android:id="@+id/mail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/username"
        android:text="Lebron James"
        android:textColor="#fff"
        android:textSize="14sp"/>

</RelativeLayout>

在代码中获取实例并监听

//初始化控件及控件监听
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBar actionBar = getSupportActionBar();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
if(actionBar != null) {
    actionBar.setDisplayHomeAsUpEnabled(true);
    actionBar.setHomeAsUpIndicator(R.drawable.menu_32px);
}
navigationView.setItemIconTintList(null);
navigationView.setCheckedItem(R.id.nav_call);//默认选项
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
        switch (menuItem.getItemId()) {
            case R.id.nav_call:
                Toast.makeText(MainActivity.this, "Call", Toast.LENGTH_SHORT).show();
                break;
                default:break;
        }
        mDrawerLayout.closeDrawers();
        return true;
    }
});
CircleImageView circleImageView = (CircleImageView) navigationView.getHeaderView(0).findViewById(R.id.icon_image);
circleImageView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(MainActivity.this, "circleImageView", Toast.LENGTH_SHORT).show();
    }
});

//设置打开滑动菜单的操作
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
    switch (item.getItemId()) {
        //... 其他菜单控件
        case android.R.id.home:
            mDrawerLayout.openDrawer(GravityCompat.START);
            break;
            default:break;
    }
    return true;
}

悬浮按钮

使用 FloatingActionButton 控件可以轻松地实现悬浮按钮的效果。

添加控件布局

<com.google.android.material.floatingactionbutton.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|end"
    android:layout_margin="16dp"
    android:src="@drawable/cloud_done"
    app:backgroundTint="#fff"
    app:elevation="8dp"
    />

处理点击事件

Snackbar一个提示工具,使用方法可以参考下面

FloatingActionButton fab = (FloatingActionButton)findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Snackbar.make(view, "Data Deleted", Snackbar.LENGTH_SHORT)
                .setAction("Undo", new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Toast.makeText(MainActivity.this, "Data restored", Toast.LENGTH_SHORT).show();
                    }
                }).show();
    }
});

卡片式布局

CardView 是用来实现卡片式布局的重要空间,实质上也是一个 FrameLayout,额外的提供了圆角和阴影效果,看上去更立体;

新建 CardView 布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
    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"
    android:layout_margin="5dp"
    app:cardCornerRadius="4dp">
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="100dp">
        <ImageView
            android:id="@+id/fruit_image"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:scaleType="center"/>
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="100dp">
            <TextView
                android:id="@+id/data"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="24sp"/>
            <TextView
                android:id="@+id/fruit_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="16sp"/>
        </LinearLayout>
    </LinearLayout>
</androidx.cardview.widget.CardView>

利用 RecyclerView 去装 CardView,所以需要为 RecyclerView 准备一个适配器

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {

    private Context mContext;
    private List<Fruit> mFruitList;

    static class ViewHolder extends RecyclerView.ViewHolder{
        CardView cardView;
        ImageView fruitImage;
        TextView fruitName;
        TextView date;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            cardView = (CardView) itemView;
            fruitImage = (ImageView) itemView.findViewById(R.id.fruit_image);
            fruitName = (TextView) itemView.findViewById(R.id.fruit_name);
            date = (TextView) itemView.findViewById(R.id.data);
        }
    }

    public FruitAdapter(List<Fruit> fruitList) {
        mFruitList = fruitList;
    }

    @NonNull
    @Override
    public 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);
        final ViewHolder holder = new ViewHolder(view);
        holder.cardView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int position = holder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                Intent intent = new Intent(mContext, FruitActivity.class);
                intent.putExtra(FruitActivity.FRUIT_NAME,fruit.getName());
                intent.putExtra(FruitActivity.FRUIT_IMAGE_ID,fruit.getImageId());
                mContext.startActivity(intent);
            }
        });
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Fruit fruit = mFruitList.get(position);
        holder.fruitName.setText(fruit.getName());
        Glide.with(mContext).load(fruit.getImageId()).into(holder.fruitImage);
        /**
         * add date
         */
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日");
        // 获取当前时间
        Date date = new Date(System.currentTimeMillis());
        holder.date.setText(simpleDateFormat.format(date));
    }

    @Override
    public int getItemCount() {
        return mFruitList.size();
    }
}

初始化 FruitAdapter 去实例 recyclerView

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 1);
recyclerView.setLayoutManager(gridLayoutManager);
adapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);

下拉刷新

利用 SwipeRefreshLayout 实现下拉刷新;

将 RecyclerView 放到该布局下,既可让 RecyclerView 具有下拉刷新功能。

在代码中添加处理逻辑

swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
swipeRefresh.setColorSchemeResources(R.color.colorPrimary);
swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
    @Override
    public void onRefresh() {
        refreshFruits();
    }
});

可折叠式标题栏

CollapsingToolbarLayout 是在 Toolbar 基础上让其更加丰富,不仅仅展示标题栏,更能实现非常华丽的效果。

CollapsingToolbarLayout 只能作为 AppBarLayout 的直接子布局使用,而 AppBarLayout 又必须是 CoordinatorLayout 的子布局。

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    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:fitsSystemWindows="true">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appBar"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:fitsSystemWindows="true">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:id="@+id/fruit_image_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="center"
                android:fitsSystemWindows="true"
                app:layout_collapseMode="parallax"/>

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar1"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin">

            </androidx.appcompat.widget.Toolbar>

        </com.google.android.material.appbar.CollapsingToolbarLayout>

    </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">

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <androidx.cardview.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="15dp"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="15dp"
                android:layout_marginTop="35dp"
                app:cardCornerRadius="4dp">

                <TextView
                    android:id="@+id/fruit_content_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="10dp"/>
            </androidx.cardview.widget.CardView>

        </LinearLayout>

    </androidx.core.widget.NestedScrollView>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fruit_fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:src="@drawable/content"
        app:backgroundTint="#fff"
        app:layout_anchor="@id/appBar"
        app:layout_anchorGravity="bottom|end"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

控件获取实例初始化

Intent intent = getIntent();
String fruitName = intent.getStringExtra(FRUIT_NAME);
int fruitImageId = intent.getIntExtra(FRUIT_IMAGE_ID, 0);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar1);
CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout)
        findViewById(R.id.collapsing_toolbar);
ImageView fruitImageView = (ImageView) findViewById(R.id.fruit_image_view);
TextView fruitContentText = (TextView) findViewById(R.id.fruit_content_text);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null) {
    actionBar.setDisplayHomeAsUpEnabled(true);
}
collapsingToolbarLayout.setTitle(fruitName);
Glide.with(this).load(fruitImageId).into(fruitImageView);
String fruitContent = generateFruitContent(fruitName);
fruitContentText.setText(fruitContent);

FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fruit_fab);
fab.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Toast.makeText(FruitActivity.this, "Comment this", Toast.LENGTH_SHORT).show();
    }
});

LogUtil

管理 Log 的工具

public class LogUtil {
    public static final String tag = "zlLog";
    public static final int VERBOSE = 1;
    public static final int DEBUG = 2;
    public static final int INFO = 3;
    public static final int WARN = 4;
    public static final int ERROR = 5;
    public static final int NOTHING = 6;
    public static int level = VERBOSE;

    public static void v(String msg) {
        if(level <= VERBOSE) {
            Log.v(tag, msg);
        }
    }
    public static void d(String msg) {
        if(level <= DEBUG) {
            Log.v(tag, msg);
        }
    }
    public static void i(String msg) {
        if(level <= INFO) {
            Log.v(tag, msg);
        }
    }
    public static void w(String msg) {
        if(level <= WARN) {
            Log.v(tag, msg);
        }
    }
    public static void e(String msg) {
        if(level <= ERROR) {
            Log.v(tag, msg);
        }
    }
}

项目源码下载

Github

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值