第一行代码 第十二章(最佳UI体验)


最佳UI体验

其实长久以来,大多数人都认为Android系统的UI并不算美观,至少没有 iOS系统的美观。以至于很多IT公司在进行应用界面设计的时候,为了保证双平台的统一性,强制要求Android端的界面风格必须和 iOS端一致。同一个操作系统中各个应用之间的界面统一性要远比一个应用在双平台的界面统一性重要得多,只有这样,才能给使用者带来更好的用户体验。
但问题在于,Android标准的界面设计风格并不是特别被大众所接受,很多公司都觉得自己完全可以设计出更加好看的界面,从而导致Android平台的界面风格长期难以得到统一。在2014年Google IO大会上重磅推出了一套全新的界面设计语言-—-—Material Design。
本章我们就将对Material Design进行一次深入的学习。

一、什么是 Material Design

Material Design是由谷歌的设计工程师基于优秀的设计原则,结合丰富的创意和科学技术所发明的一套全新的界面设计语言,包含了视觉、运动、互动效果等特性。

二、布局设计

1.布局代码分别如下
在这里插入图片描述
代码如下:

<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">
    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <androidx.appcompat.widget.Toolbar
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:id="@+id/toolbar"
                android:background="?attr/colorPrimary"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:layout_scrollFlags="scroll|enterAlways|snap"
                />
            <!-- layout_scrollFlags :scroll表示向上滚动时Toolbar滚动并实现隐藏,enenterAlways表示向下滚动时Toolabr向下滚动并重现
                   snap表示当Toolbar还没有完全隐藏或显示时,根据当前滚动的距离,选择隐藏还是显示 -->
        </com.google.android.material.appbar.AppBarLayout>
        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/srf"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <androidx.recyclerview.widget.RecyclerView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/recyclerview"
                />

        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>


        <!-- 指定布局行为 -->
        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/fab"
            android:src="@drawable/pear"
            android:layout_gravity="bottom|right"
            android:layout_margin="16dp"
            app:elevation="8dp"

            />
        <!-- elevation给FloatingActionButton指定一个高度值,高度值越大,投影范围越大,投影效果越淡 -->
    </androidx.coordinatorlayout.widget.CoordinatorLayout>


    <com.google.android.material.navigation.NavigationView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/nav_view"
        android:layout_gravity="start"
        app:menu="@menu/circleimagemenu"
        app:headerLayout="@layout/headerlayout"/>

</androidx.drawerlayout.widget.DrawerLayout>

activity_main2.xml代码如下:

<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"
    >
    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:id="@+id/appbar">
        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:contentScrim="@color/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">
            <!--exitUntilCollapsed随着滚动完成折叠之后就保留在界面上,不在移出屏幕 -->
            <ImageView
                android:id="@+id/image2"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                app:layout_collapseMode="parallax"/>
            <androidx.appcompat.widget.Toolbar
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:id="@+id/toolbar2"
                app:layout_collapseMode="pin"/>
        </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:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <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/textview2"
                    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:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:src="@drawable/pear"
        app:layout_anchor="@id/toolbar2"
        app:layout_anchorGravity="bottom|end"/>
    <!--layout_anchor设置瞄点 -->



</androidx.coordinatorlayout.widget.CoordinatorLayout>

headerlayout.xml代码如下:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="180dp">          <!-- 180dp是NavigationView比较合适的高度 -->
    <de.hdodenhof.circleimageview.CircleImageView
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:id="@+id/header_image"
        android:src="@drawable/pear"
        android:layout_centerInParent="true"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/header_textView1"
        android:text="嘿嘿"
        android:textSize="14sp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:textColor="#191515"
        />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/header_textView2"
        android:text="哎呀"
        android:textSize="14sp"
        android:layout_above="@id/header_textView1"
        android:textColor="#150A0A"
        />


</RelativeLayout>

item.xml代码

<androidx.cardview.widget.CardView 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:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:id="@+id/item_image"
            android:scaleType="centerCrop"/>      <!-- 指定图片的缩放模式,他可以让图片保持原有比例填充ImageView,并裁掉超出部分 -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/item_textview"
            android:layout_gravity="center_horizontal"
            android:layout_margin="5dp"
            android:textSize="16sp"/>
    </LinearLayout>


</androidx.cardview.widget.CardView>

三、程序设计

1.分别创建3个文件夹如(Fruit)、(MainActivity2)、 (Mydapt)
在这里插入图片描述
MainActivity代码


import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;

import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.navigation.NavigationView;
import com.google.android.material.snackbar.Snackbar;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;




public class MainActivity extends AppCompatActivity {
    DrawerLayout drawerLayout;
    private SwipeRefreshLayout swipeRefreshLayout;
    private Mydapt adapt;
    private List<Fruit> list = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        NavigationView navigationView = (NavigationView)findViewById(R.id.nav_view);
        Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);

        setSupportActionBar(toolbar);
        drawerLayout = (DrawerLayout)findViewById(R.id.drawerLayout);
        ActionBar actionBar = getSupportActionBar();
        if(actionBar != null){
            actionBar.setDisplayHomeAsUpEnabled(true);      //让导航按钮显示出来
            actionBar.setHomeAsUpIndicator(R.mipmap.ic_launcher);   //设置导航按钮图标,默认是返回的图标
        }
        navigationView.setCheckedItem(R.id.item_cgl1);      //设置菜单项的默认选中
        //对菜单项设置监听
        navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
                drawerLayout.closeDrawers();        //将滑动菜单关闭
                return true;
            }
        });
        //FloatingActionButton点击事件
        FloatingActionButton floatingActionButton = (FloatingActionButton)findViewById(R.id.fab);
        floatingActionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Snackbar.make(v,"Data delete",Snackbar.LENGTH_SHORT)
                        .setAction("Undo", new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                Toast.makeText(MainActivity.this,"click",Toast.LENGTH_SHORT).show();
                            }
                        })
                        .show();

            }
        });
        //RecyclerView
        addlist();
        RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recyclerview);
        GridLayoutManager manager = new GridLayoutManager(MainActivity.this,2);
        adapt = new Mydapt(list);
        recyclerView.setLayoutManager(manager);
        recyclerView.setAdapter(adapt);
        //下拉刷新:
        swipeRefreshLayout = (SwipeRefreshLayout)findViewById(R.id.srf);
        swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary);       //设置颜色
        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                refreshFruits();
            }
        });

    }

    private void refreshFruits() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        addlist();
                        adapt.notifyDataSetChanged();
                        swipeRefreshLayout.setRefreshing(false);        //刷新事件结束,并隐藏刷新进度条
                    }
                });
            }
        }).start();
    }

    private void addlist() {
        list.clear();
        Fruit[] fruits = {new Fruit("grape",R.drawable.grape),new Fruit("orange",R.drawable.orange),
                new Fruit("pear",R.drawable.pear),new Fruit("watemelon",R.drawable.watemelon)};
        for(int i  =0; i < 50 ;i++){
            Random random = new Random();
            int index = random.nextInt(fruits.length);
            list.add(fruits[index]);
        }
    }

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

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()){
            case R.id.item1:
                break;
            case R.id.item2:
                break;
            case R.id.item3:
                break;
            case android.R.id.home:
                drawerLayout.openDrawer(GravityCompat.START);
        }
        return true;
    }
}

Fruit代码代码如下:


public class Fruit {
    private String Name;
    private int imageId;

    public Fruit(String name, int imageId) {
        Name = name;
        this.imageId = imageId;
    }

    public String getName() {
        return Name;
    }

    public int getImageId() {
        return imageId;
    }
}

MainActivity2代码


import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

import android.content.Intent;
import android.os.Bundle;
import android.view.MenuItem;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.google.android.material.appbar.CollapsingToolbarLayout;

public class MainActivity2 extends AppCompatActivity {
    public static final String FRUIT_NAME = "fruit_name";
    public static final String FRUIT_IMAGE_ID = "fruit_image_id";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        Intent intent = getIntent();
        String fruitName = intent.getStringExtra(FRUIT_NAME);
        int imageId = intent.getIntExtra(FRUIT_IMAGE_ID,0);

        Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar2);
        CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout)findViewById(R.id.collapsing_toolbar);
        ImageView imageView = (ImageView)findViewById(R.id.image2);
        TextView textView = (TextView)findViewById(R.id.textview2);
        setSupportActionBar(toolbar);
        ActionBar actionBar = getSupportActionBar();
        if(actionBar != null){
            actionBar.setDisplayHomeAsUpEnabled(true);
        }
        collapsingToolbarLayout.setTitle(fruitName);
        Glide.with(this).load(imageId).into(imageView);
        String fruitContent = geterateFruitContent(fruitName);
        textView.setText(fruitContent);
    }

    private String geterateFruitContent(String fruitName) {
        StringBuilder fruitContent = new StringBuilder();
        for(int i = 0;  i < 500; i++){
            fruitContent.append(fruitName);
        }
        fruitContent.reverse();
        return fruitContent.toString();
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()){
            case android.R.id.home:
                finish();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

Mydapt代码


import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.Glide;

import org.w3c.dom.Text;

import java.util.List;

public class Mydapt extends RecyclerView.Adapter<Mydapt.ViewHolder> {
    private List<Fruit> list;
    private Context mcontext;
    public Mydapt(List<Fruit> list) {
        this.list = list;
    }

    class ViewHolder extends RecyclerView.ViewHolder{
        CardView cardView;
        ImageView imageView;
        TextView textView;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            cardView = (CardView) itemView;
            imageView = itemView.findViewById(R.id.item_image);
            textView = itemView.findViewById(R.id.item_textview);
        }
    }
    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if(mcontext==null)
            mcontext = parent.getContext();
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
        final ViewHolder viewHolder = new ViewHolder(view);
        viewHolder.cardView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = viewHolder.getAdapterPosition();
                Fruit fruit = list.get(position);
                Intent intent = new Intent(mcontext,MainActivity2.class);
                intent.putExtra(MainActivity2.FRUIT_NAME,fruit.getName());
                intent.putExtra(MainActivity2.FRUIT_IMAGE_ID,fruit.getImageId());
                mcontext.startActivity(intent);
            }
        });
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Fruit fruit = list.get(position);
        holder.textView.setText(fruit.getName());
        Glide.with(mcontext).load(fruit.getImageId()).into(holder.imageView);       //用Glide加载图片

    }

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


}

(一)修改标题栏显示文字的内容

1、修改AndroidManifest中< activity>的label属性

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.revise_12_1">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity2"></activity>
        <activity
            android:name=".MainActivity"
            android:label="this ok">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

(二)下拉刷新

在 Meterial Design 中,SwipeRefreshLayout 是用于实现下拉刷新的核心类,它由 support-v4 库提供,把要实现下拉刷新功能的控件放置到 SwipeRefreshLayout 中,就能让这个控件支持下拉刷新。

在内容外嵌套:

<android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/swipe_refresh"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
 
            <!--appbar_scrolling_view_behavior 唯一的作用是把自己(使用者)放到AppBarLayout的下面-->
            <android.support.v7.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
 
        </android.support.v4.widget.SwipeRefreshLayout>

上述代码值得注意的是,由于 RecyclerView 变成了 SwipeRefreshLayout 的子控件,因此之前用 app:layout_behavior 声明布局行为要移到 SwipeRefreshLayout 中才行。

修改活动:

// 下拉刷新
        swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
        swipeRefresh.setColorSchemeResources(R.color.colorPrimary);//设置刷新进度条颜色
        swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                // 处理刷新逻辑
                refreshPartner();
            }
        });

处理刷新逻辑(模拟)

/**
     * 下拉刷新数据(为简单起见没和网络交互)
     */
    private void refreshPartner() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        initPartner();//重新生成数据
                        adapter.notifyDataSetChanged();//通知数据变化
                        swipeRefresh.setRefreshing(false);//隐藏刷新进度条
                    }
                });
            }
        }).start();
    }

四.程序运行

1.虚拟机上运行结果

在这里插入图片描述

2.真实机上运行结果

在这里插入图片描述


总结

学完了本章的所有知识,你有没有觉得无比兴奋呢?反正我是这么觉得的。本章我们的收获实在是太多了,从一个什么都没有的空项目,经过一章的学习,最后实现了一个功能如此丰富、界面如此华丽的应用,还有什么事情比这个更让我们有成就感吗?
本章中我们充分利用了Design Support库、support-v4库、appcompat-v7库,以及一些开源项目来实现一个了高度Material化的应用程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值