Material Design
Snackbar
-
Snackbar 是一种轻量级的弹出框,有一个Title,并且比Toast多一个有点击功能的按钮;通常显示在屏幕的底部并在屏幕所有层的最上方,包括浮动操作按钮。它会在超时后自动消失。
-
代码实现<项目:c12materialtest>:
Snackbar.make(view, "Data delete", Snackbar.LENGTH_SHORT) .setAction("Undo", new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "Data restore", Toast.LENGTH_SHORT).show(); } }).show();
-
注意:Snackbar的make方法的第一个参数是Snackbar的父控件,是一个View类型的参数;第一个参数是 Snackbar 的 Title;setAction 中设置按钮的点击事件;
TextlnputLayout
-
TextInputLayout 控件是一个容器,它跟ScrollView一样只接受一个子元素,并且这个子元素是 EditText。
-
按照常规的< EditText>做法,使用 “hint” 在我们未输入时会显示出来,输入的时候就会消失;如图<项目:myapplication200122>:
-
使用 TextlnputLayout 设置的hint值不在原本的EditText中,而是浮在上方,当我们将焦点移动到输入password的输入框时输入框上浮的文字回到原本的EditText中并伴有动画;代码如下所示:
添加依赖: 在 app 目录下的 build.gradle 中添加“ implementation 'com.android.support:design:28.0.0' ”
<com.google.android.material.textfield.TextInputLayout android:id="@+id/password_til" android:layout_width="match_parent" android:layout_height="wrap_content" app:errorEnabled="true" android:layout_margin="10dp" app:hintTextAppearance="R.style.hintAppearence" android:focusable="false" app:counterEnabled="true" app:counterMaxLength="10" android:focusableInTouchMode="false" android:hint="请输入密码"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textPassword" android:paddingLeft="10dp" /> </com.google.android.material.textfield.TextInputLayout>
//代码中;String password= password_til.getEditText().getText().toString(); TextInputLayout password_til = findViewById(R.id.password_til); password_til.setPasswordVisibilityToggleEnabled(true); //设置 浮动标签样式 password_til.setHintTextAppearance(R.style.hintAppearence);
此外还可以通过:passwordTIL.setError("账号或密码错误");
代替 Toast 来提示密码错误
注:对 EditText 的监听还是需要添加 EditText 的监听器,在其中做 setError;
存在的问题,输入了账号密码,hint值没有消失?下面是理想效果。
TabLayout
实现选项卡的动态滑动效果<项目:myapplication200122>。
-
添加依赖:
implementation ‘androidx.cardview:cardview:1.0.0’
implementation ‘androidx.recyclerview:recyclerview:1.2.0-alpha01’ -
主界面布局
<LinearLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".tablayout_test.TabLayoutTest">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.example.myapplication200122.TitleLayout
android:id="@+id/tab_title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorColor="#ADBE107E"
app:tabMode="scrollable" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/tab_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
注意:app:tabMode="scrollable",Tab的模式为 可滑动的”。设置 app:tabMode="fixed",表示固定不可滑动的
-
Java代码
MainActivity/* * 设定标题内容; * 创建相应的 TabLayout 和 Fragment, * 设置 ViewPager 适配器和 TabLayout 适配器, * 将TabLayout 和 ViewPager关联起来 * / public class TabLayoutTest extends AppCompatActivity { private ViewPager mViewPager; private TabLayout mTabLayout; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tab_layout); mViewPager = findViewById(R.id.tab_viewpager); initViewPager(); } private void initViewPager() { mTabLayout = findViewById(R.id.tabs); List<String> titles = new ArrayList<>(); titles.add("推荐"); titles.add("要闻"); titles.add("新思想"); titles.add("北京"); titles.add("综合"); titles.add("直播中国"); for (int i = 0; i < titles.size(); i++) { mTabLayout.addTab(mTabLayout.newTab().setText(titles.get(i))); } List<Fragment> fragments = new ArrayList<>(); for (int i = 0; i < titles.size(); i++) { fragments.add(new ListFragment()); } /** * 自定义的 adapter */ FragmentAdapter mFragmentAdapter = new FragmentAdapter(getSupportFragmentManager(),fragments,titles); /** * 给 ViewPager 设置适配器 */ mViewPager.setAdapter(mFragmentAdapter); /** * 将 TabLayout 和 ViewPager 关联起来 */ mTabLayout.setupWithViewPager(mViewPager); /** * 给 TabLayout 设置适配器 */ mTabLayout.setTabsFromPagerAdapter(mFragmentAdapter); } }
adapter
/** * 根据不同的 position 来返回不同的Fragment; * 根据不同的position来返回不同的title */ public class FragmentAdapter extends FragmentStatePagerAdapter { private List<Fragment> mFragments; private List<String> mTitles; public FragmentAdapter(@NonNull FragmentManager fm, List<Fragment> fragments, List<String> titles) { super(fm); mFragments = fragments; mTitles = titles; } @NonNull @Override public Fragment getItem(int position) { return mFragments.get(position); } @Override public int getCount() { return mFragments.size(); } @Nullable @Override public CharSequence getPageTitle(int position) { return mTitles.get(position); } }
ListFragment
/** * 系统有 ListFragment ;但这是自己的;作为每个Tab的界面;用RecyclerView来代替ListView */ public class ListFragment extends androidx.fragment.app.Fragment { private RecyclerView mRecyclerView; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { mRecyclerView = (RecyclerView) inflater.inflate( R.layout.list_fragment,container, flase); return mRecyclerView; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mRecyclerView.setLayoutManager(new LinearLayoutManager(mRecyclerView.getContext())); mRecyclerView.setAdapter(new RecyclerViewAdapter(getActivity())); } }
为什么这么丑?审美受限而已……
NavigationView
-
抽屉菜单界面<项目:c12materialtest>布局文件的根布局是DrawerLayout;菜单也写在NavigationView里。
-
布局文件如下:
<?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"> <!-- Coordinatorlayout 是一个加强版的 FrameLayout;可监听 其所有<子>控件的各种事件, 以解决类似 SnackBar 遮住 FloaActionButton--> <androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <!-- AppBarLayout 是一个 垂直方向 LinearLayout 解决 RecyclerView 遮住了 ToolBar --> <com.google.android.material.appbar.AppBarLayout android:id="@+id/appBarLayout" android:layout_width="match_parent" android:layout_height="wrap_content"> <!-- app:layout_scrollFlags="scroll|enterAlways|snap" scroll 表示RecyclerView向上滚动时,ToolBar 会跟着一起向上滚动并实现隐藏 enterAlways 表示RecyclerView向下滚动时,ToolBar 会跟着一起向下滚动并重新显示 snap 表示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:layout_anchor="@+id/appBarLayout" app:layout_anchorGravity="center" app:layout_scrollFlags="scroll|enterAlways|snap" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> </com.google.android.material.appbar.AppBarLayout> <!-- SwipeRefreshLayout 实现下拉刷新功能 --> <androidx.swiperefreshlayout.widget.SwipeRefreshLayout android:id="@+id/swipe_frash" 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" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="16dp" android:elevation="8dp" android:src="@drawable/ic_done" /> </androidx.coordinatorlayout.widget.CoordinatorLayout> <!-- app:headerLayout 引入头布局<图片部分>--> <!--app:menu 引入菜单布局--> <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:headerLayout="@layout/nav_header" app:menu="@menu/nav_menu" /> </androidx.drawerlayout.widget.DrawerLayout>
-
nav_menu.xml
<?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/nav_call" android:title="Call" /> <item android:id="@+id/nav_friends" android:icon="@drawable/nav_friends" android:title="Friends" /> <item android:id="@+id/nav_location" android:icon="@drawable/nav_location" android:title="Location" /> <item android:id="@+id/nav_mail" android:icon="@drawable/nav_mail" android:title="Mail" /> <item android:id="@+id/nav_task" android:icon="@drawable/nav_task" android:title="Task" /> </group> <!-- 通过为item添加子菜单来实现带有头部的分组效果--> <item android:title="其他"> <menu> <item android:title="设置" android:icon="@drawable/ic_dashboard"/> <item android:title="关于我们" android:icon="@drawable/ic_dashboard"/> </menu> </item> </menu>
Java代码:setNavigationItemSelectedListener()方法来设置当导航项被点击时的回调; @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: //HomeAsUp 按钮的id永远都是android.R.id.home mDrawerLayout.openDrawer(GravityCompat.START); //将滑动菜单展示出来 break; ...... /** * 设置监听器;当用户 点击了 任意菜单项时,就会回调 onNavigationItemSelected(); */ navView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) { mDrawerLayout.closeDrawers(); return true; }
CoordinatorLayout
CoordinatorLayout中最经典的设计就是的Behavior;下次见;