日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

Android 5.x Theme 与 ToolBar 实战

發(fā)布時(shí)間:2025/3/8 83 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android 5.x Theme 与 ToolBar 实战 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(zhuǎn)載請(qǐng)標(biāo)明出處:?

http://blog.csdn.net/lmj623565791/article/details/45303349;?
本文出自:【張鴻洋的博客】

1、概述

隨著Material Design的逐漸的普及,業(yè)內(nèi)也有很多具有分享精神的伙伴翻譯了material design specification?,中文翻譯地址:Material Design 中文版。So,我們也開(kāi)始Android?5.x相關(guān)的blog,那么首先了解的當(dāng)然是其主題的風(fēng)格以及app bar。

當(dāng)然,5.x普及可能還需要一段時(shí)間,所以我們還是盡可能的去使用兼容包支持低版本的設(shè)備。

ps:本博客使用:

  • compileSdkVersion 22
  • buildToolsVersion “22.0.1”
  • compile ‘com.android.support:appcompat-v7:22.1.1’
  • 忽然發(fā)現(xiàn)ActionBarActivity被棄用了,推薦使用AppCompatActivity,相關(guān)blog地址:Android Support Library 22.1

2、Material Design的Theme

md的主題有:

  • @android:style/Theme.Material (dark version)
  • @android:style/Theme.Material.Light (light version)
  • @android:style/Theme.Material.Light.DarkActionBar

與之對(duì)應(yīng)的Compat Theme:

  • Theme.AppCompat
  • Theme.AppCompat.Light
  • Theme.AppCompat.Light.DarkActionBar

(1)個(gè)性化 Color Palette

我們可以根據(jù)我們的app的風(fēng)格,去定制Color Palette(調(diào)色板),重點(diǎn)有以下幾個(gè)屬性:

<resources><!-- Base application theme. --><style name="AppBaseTheme" parent="Theme.AppCompat"> <!-- customize the color palette --> <item name="colorPrimary">@color/material_blue_500</item> <item name="colorPrimaryDark">@color/material_blue_700</item> <item name="colorAccent">@color/material_green_A200</item> </style> </resources>

?

  • colorPrimary 對(duì)應(yīng)ActionBar的顏色。
  • colorPrimaryDark對(duì)應(yīng)狀態(tài)欄的顏色
  • colorAccent 對(duì)應(yīng)EditText編輯時(shí)、RadioButton選中、CheckBox等選中時(shí)的顏色。

與之對(duì)應(yīng)的圖:

metarial design的theme允許我們?nèi)ピO(shè)置status bar的顏色,如果你項(xiàng)目的最小支持版本為5.0,那么你可以使用android:Theme.Material,設(shè)置android:statusBarColor。當(dāng)然了這種情況目前來(lái)說(shuō)比較少,所以我們多數(shù)使用的是Theme.AppCompat,通過(guò)設(shè)置android:colorPrimaryDark.來(lái)設(shè)置status bar顏色。(ps:默認(rèn)情況下,android:statusBarColor的值繼承自android:colorPrimaryDark).

對(duì)于5.0以下的設(shè)備,目前colorPrimaryDark無(wú)法去個(gè)性化狀態(tài)欄的顏色;底部的navagationBar可能也不一樣,更別說(shuō)設(shè)置顏色了。

下面寫(xiě)個(gè)簡(jiǎn)單的Demo去測(cè)試下。

(2)測(cè)試效果

values/styles.xml

<resources><!-- Base application theme. --><style name="AppTheme" parent="AppBaseTheme"> </style> <style name="AppBaseTheme" parent="Theme.AppCompat.Light"> <!-- customize the color palette --> <item name="colorPrimary">@color/material_blue_500</item> <item name="colorPrimaryDark">@color/material_blue_700</item> <item name="colorAccent">@color/material_green_A200</item> </style> </resources>

?

values-v21/styles.xml

<resources><style name="AppTheme" parent="AppBaseTheme"> <item name="android:statusBarColor">@color/material_blue_700</item> </style> </resources>

?

values/colors.xml

<?xml version="1.0" encoding="utf-8"?> <resources><color name="material_blue_500">#009688</color> <color name="material_blue_700">#00796B</color> <color name="material_green_A200">#FD87A9</color> </resources>

?

可以看到:colorAccent也就是圖中的粉色,EditText正在輸入時(shí),RadioButton選中時(shí)的顏色。ps:5.0以下設(shè)備,狀態(tài)欄顏色不會(huì)變化。

3、ToolBar的使用

眾所周知,在使用ActionBar的時(shí)候,一堆的問(wèn)題:這個(gè)文字能不能定制,位置能不能改變,圖標(biāo)的間距怎么控制神馬的,由此暴露出了ActionBar設(shè)計(jì)的不靈活。為此官方提供了ToolBar,并且提供了supprot library用于向下兼容。Toolbar之所以靈活,是因?yàn)樗鋵?shí)就是一個(gè)ViewGroup,我們?cè)谑褂玫臅r(shí)候和普通的組件一樣,在布局文件中聲明。

(1)ToolBar的引入

既然準(zhǔn)備用ToolBar,首先看看如何將其引入到app中。

1)隱藏原本的ActionBar

隱藏可以通過(guò)修改我們繼承的主題為:Theme.AppCompat.Light.NoActionBar,當(dāng)然也可以通過(guò)設(shè)置以下屬性完成:

<item name="windowActionBar">false</item> <item name="android:windowNoTitle">true</item>

?

我們這里選擇前者:

<style name="AppBaseTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- customize the color palette --> <item name="colorPrimary">@color/material_blue_500</item> <item name="colorPrimaryDark">@color/material_blue_700</item> <item name="colorAccent">@color/material_green_A200</item> </style>

?

2)在布局文件中聲明
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent" android:orientation="vertical" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"> <android.support.v7.widget.Toolbar android:id="@+id/id_toolbar" android:layout_height="wrap_content" android:layout_width="match_parent" /> <android.support.v7.widget.GridLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" app:useDefaultMargins="true" app:columnCount="3"> <TextView android:text="First Name:" app:layout_gravity="right" /> <EditText android:ems="10" app:layout_columnSpan="2" /> <TextView android:text="Last Name:" app:layout_column="0" app:layout_gravity="right" /> <EditText android:ems="10" app:layout_columnSpan="2" /> <TextView android:text="Visit Type:" app:layout_column="0" app:layout_gravity="right" /> <RadioGroup app:layout_columnSpan="2"> <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Business" /> <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Social" /> </RadioGroup> <Button android:text="Ok" app:layout_column="1" /> <Button android:text="Cancel" app:layout_column="2" /> </android.support.v7.widget.GridLayout> </LinearLayout>

?

ok,這里我們也貼出來(lái)上面圖片的效果的xml,使用GridLayout實(shí)現(xiàn)的,有興趣的可以研究下??梢钥吹轿覀?cè)诓季治募卸x了ToolBar。

3)代碼中設(shè)定
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.id_toolbar); setSupportActionBar(toolbar); }

?

ok,基本就是先隱藏ActionBar,然后在布局文件中聲明,最后代碼中設(shè)定一下?,F(xiàn)在看一下效果圖:

可以看到我們的ToolBar顯示出來(lái)了,默認(rèn)的Title為T(mén)oolBar,但是這個(gè)樣式實(shí)在是不敢恭維,下面看我們?nèi)绾味ㄖ扑?/p>

(2)定制ToolBar

首先給它一個(gè)nice的背景色,還記得前面的colorPrimary么,用于控制ActionBar的背景色的。當(dāng)然這里我們的ToolBar就是一個(gè)普通的ViewGroup在布局中,所以我們直接使用background就好,值可以為:?attr/colorPrimary使用主題中定義的值。

ToolBar中包含Nav Icon , Logo , Title , Sub Title , Menu Items 。

我們可以通過(guò)代碼設(shè)置上述ToolBar中的控件:

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.id_toolbar); // App Logo toolbar.setLogo(R.mipmap.ic_launcher); // Title toolbar.setTitle("App Title"); // Sub Title toolbar.setSubtitle("Sub title"); setSupportActionBar(toolbar); //Navigation Icon toolbar.setNavigationIcon(R.drawable.ic_toc_white_24dp); }

?

可選方案當(dāng)然如果你喜歡,也可以在布局文件中去設(shè)置部分屬性:

<android.support.v7.widget.Toolbarandroid:id="@+id/id_toolbar"app:title="App Title" app:subtitle="Sub Title" app:navigationIcon="@drawable/ic_toc_white_24dp" android:layout_height="wrap_content" android:minHeight="?attr/actionBarSize" android:layout_width="match_parent" android:background="?attr/colorPrimary"/>

?

至于Menu Item,依然支持在menu/menu_main.xml去聲明,然后復(fù)寫(xiě)onCreateOptionsMenu和onOptionsItemSelected即可。

可選方案也可以通過(guò)toolbar.setOnMenuItemClickListener實(shí)現(xiàn)點(diǎn)擊MenuItem的回調(diào)。

toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {@Overridepublic boolean onMenuItemClick(MenuItem item) { return false; } });

?

效果圖:

關(guān)于字體的樣式,可以在布局文件設(shè)置屬性app:titleTextAppearance、app:subtitleTextAppearance或者代碼setTitleTextAppearance、setSubTitleTextAppearance設(shè)置。

4、實(shí)戰(zhàn)

簡(jiǎn)單介紹了Toolbar以后呢,我們決定做點(diǎn)有意思的事,整合ToolBar,DrawerLayout,ActionBarDrawerToggle寫(xiě)個(gè)實(shí)用的例子,效果圖如下:

ok,簡(jiǎn)單處理了下橫縱屏幕的切換。接下來(lái)看代碼實(shí)現(xiàn)。

  • 大致思路

整體實(shí)現(xiàn)還是比較容易的,首先需要引入DrawerLayout(如果你對(duì)DrawerLayout不了解,可以參考?
Android DrawerLayout 高仿QQ5.2雙向側(cè)滑菜單),然后去初始化mActionBarDrawerToggle,mActionBarDrawerToggle實(shí)際上是個(gè)DrawerListener,設(shè)置mDrawerLayout.setDrawerListener(mActionBarDrawerToggle);就已經(jīng)能夠?qū)崿F(xiàn)上面點(diǎn)擊Nav Icon切換效果了。當(dāng)然了細(xì)節(jié)還是挺多的。

我們的效果圖,左側(cè)菜單為Fragment,內(nèi)容區(qū)域?yàn)镕ragment,點(diǎn)擊左側(cè)菜單切換內(nèi)容區(qū)域的Fragment即可。關(guān)于Fragment的知識(shí),可以查看:Android Fragment 你應(yīng)該知道的一切

  • 布局文件?
    activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent" android:orientation="vertical" android:layout_height="match_parent" android:background="#ffffffff" xmlns:app="http://schemas.android.com/apk/res-auto"> <!--app:subtitle="Sub Title"--> <android.support.v7.widget.Toolbar android:id="@+id/id_toolbar" app:title="App Title" app:navigationIcon="@drawable/ic_toc_white_24dp" android:layout_height="wrap_content" android:minHeight="?attr/actionBarSize" android:layout_width="match_parent" android:background="?attr/colorPrimary" /> <android.support.v4.widget.DrawerLayout android:id="@+id/id_drawerlayout" android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:id="@+id/id_content_container" android:layout_width="match_parent" android:layout_height="match_parent"></FrameLayout> <FrameLayout android:id="@+id/id_left_menu_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="left" android:background="#ffffffff"></FrameLayout> </android.support.v4.widget.DrawerLayout> </LinearLayout>

DrawerLayout中包含兩個(gè)FrameLayout,分別放內(nèi)容區(qū)域和左側(cè)菜單的Fragment。

  • LeftMenuFragment
package com.zhy.toolbar;import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.ListFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ListView; /** * Created by zhy on 15/4/26. */ public class LeftMenuFragment extends ListFragment { private static final int SIZE_MENU_ITEM = 3; private MenuItem[] mItems = new MenuItem[SIZE_MENU_ITEM]; private LeftMenuAdapter mAdapter; private LayoutInflater mInflater; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mInflater = LayoutInflater.from(getActivity()); MenuItem menuItem = null; for (int i = 0; i < SIZE_MENU_ITEM; i++) { menuItem = new MenuItem(getResources().getStringArray(R.array.array_left_menu)[i], false, R.drawable.music_36px, R.drawable.music_36px_light); mItems[i] = menuItem; } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return super.onCreateView(inflater, container, savedInstanceState); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); setListAdapter(mAdapter = new LeftMenuAdapter(getActivity(), mItems)); } @Override public void onListItemClick(ListView l, View v, int position, long id) { super.onListItemClick(l, v, position, id); if (mMenuItemSelectedListener != null) { mMenuItemSelectedListener.menuItemSelected(((MenuItem) getListAdapter().getItem(position)).text); } mAdapter.setSelected(position); } //選擇回調(diào)的接口 public interface OnMenuItemSelectedListener { void menuItemSelected(String title); } private OnMenuItemSelectedListener mMenuItemSelectedListener; public void setOnMenuItemSelectedListener(OnMenuItemSelectedListener menuItemSelectedListener) { this.mMenuItemSelectedListener = menuItemSelectedListener; } }

?

繼承自ListFragment,主要用于展示各個(gè)Item,提供了一個(gè)選擇Item的回調(diào),這個(gè)需要在Activity中去注冊(cè)處理。

  • LeftMenuAdapter
package com.zhy.toolbar;import android.content.Context; import android.graphics.Color; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; /** * Created by zhy on 15/4/26. */ public class LeftMenuAdapter extends ArrayAdapter<MenuItem> { private LayoutInflater mInflater; private int mSelected; public LeftMenuAdapter(Context context, MenuItem[] objects) { super(context, -1, objects); mInflater = LayoutInflater.from(context); } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.item_left_menu, parent, false); } ImageView iv = (ImageView) convertView.findViewById(R.id.id_item_icon); TextView title = (TextView) convertView.findViewById(R.id.id_item_title); title.setText(getItem(position).text); iv.setImageResource(getItem(position).icon); convertView.setBackgroundColor(Color.TRANSPARENT); if (position == mSelected) { iv.setImageResource(getItem(position).iconSelected); convertView.setBackgroundColor(getContext().getResources().getColor(R.color.state_menu_item_selected)); } return convertView; } public void setSelected(int position) { this.mSelected = position; notifyDataSetChanged(); } } package com.zhy.toolbar; public class MenuItem { public MenuItem(String text, boolean isSelected, int icon, int iconSelected) { this.text = text; this.isSelected = isSelected; this.icon = icon; this.iconSelected = iconSelected; } boolean isSelected; String text; int icon; int iconSelected; }
  • 1

Adapter沒(méi)撒說(shuō)的~~提供了一個(gè)setSection方法用于設(shè)置選中Item的樣式什么的。?
接下來(lái)看ContentFragment,僅僅只是一個(gè)TextView而已,所以代碼也比較easy。

package com.zhy.toolbar;import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.text.TextUtils; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; /** * Created by zhy on 15/4/26. */ public class ContentFragment extends Fragment { public static final String KEY_TITLE = "key_title"; private String mTitle; public static ContentFragment newInstance(String title) { ContentFragment fragment = new ContentFragment(); Bundle bundle = new Bundle(); bundle.putString(KEY_TITLE, title); fragment.setArguments(bundle); return fragment; } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { TextView tv = new TextView(getActivity()); String title = (String) getArguments().get(KEY_TITLE); if (!TextUtils.isEmpty(title)) { tv.setGravity(Gravity.CENTER); tv.setTextSize(40); tv.setText(title); } return tv; } }
  • 1

提供newInstance接收一個(gè)title參數(shù)去實(shí)例化它。

最后就是我們的MainActivity了,負(fù)責(zé)管理各種Fragment。

  • MainActivity
package com.zhy.toolbar;import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.Gravity; import java.util.List; public class MainActivity extends AppCompatActivity { private ActionBarDrawerToggle mActionBarDrawerToggle; private DrawerLayout mDrawerLayout; private Toolbar mToolbar; private LeftMenuFragment mLeftMenuFragment; private ContentFragment mCurrentFragment; private String mTitle; private static final String TAG = "com.zhy.toolbar"; private static final String KEY_TITLLE = "key_title"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initToolBar(); initViews(); //恢復(fù)title restoreTitle(savedInstanceState); FragmentManager fm = getSupportFragmentManager(); //查找當(dāng)前顯示的Fragment mCurrentFragment = (ContentFragment) fm.findFragmentByTag(mTitle); if (mCurrentFragment == null) { mCurrentFragment = ContentFragment.newInstance(mTitle); fm.beginTransaction().add(R.id.id_content_container, mCurrentFragment, mTitle).commit(); } mLeftMenuFragment = (LeftMenuFragment) fm.findFragmentById(R.id.id_left_menu_container); if (mLeftMenuFragment == null) { mLeftMenuFragment = new LeftMenuFragment(); fm.beginTransaction().add(R.id.id_left_menu_container, mLeftMenuFragment).commit(); } //隱藏別的Fragment,如果存在的話 List<Fragment> fragments = fm.getFragments(); if (fragments != null) for (Fragment fragment : fragments) { if (fragment == mCurrentFragment || fragment == mLeftMenuFragment) continue; fm.beginTransaction().hide(fragment).commit(); } //設(shè)置MenuItem的選擇回調(diào) mLeftMenuFragment.setOnMenuItemSelectedListener(new LeftMenuFragment.OnMenuItemSelectedListener() { @Override public void menuItemSelected(String title) { FragmentManager fm = getSupportFragmentManager(); ContentFragment fragment = (ContentFragment) getSupportFragmentManager().findFragmentByTag(title); if (fragment == mCurrentFragment) { mDrawerLayout.closeDrawer(Gravity.LEFT); return; } FragmentTransaction transaction = fm.beginTransaction(); transaction.hide(mCurrentFragment); if (fragment == null) { fragment = ContentFragment.newInstance(title); transaction.add(R.id.id_content_container, fragment, title); } else { transaction.show(fragment); } transaction.commit(); mCurrentFragment = fragment; mTitle = title; mToolbar.setTitle(mTitle); mDrawerLayout.closeDrawer(Gravity.LEFT); } }); } private void restoreTitle(Bundle savedInstanceState) { if (savedInstanceState != null) mTitle = savedInstanceState.getString(KEY_TITLLE); if (TextUtils.isEmpty(mTitle)) { mTitle = getResources().getStringArray( R.array.array_left_menu)[0]; } mToolbar.setTitle(mTitle); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString(KEY_TITLLE, mTitle); } private void initToolBar() { Toolbar toolbar = mToolbar = (Toolbar) findViewById(R.id.id_toolbar); // App Logo // toolbar.setLogo(R.mipmap.ic_launcher); // Title toolbar.setTitle(getResources().getStringArray(R.array.array_left_menu)[0]); // Sub Title // toolbar.setSubtitle("Sub title"); // toolbar.setTitleTextAppearance(); setSupportActionBar(toolbar); //Navigation Icon toolbar.setNavigationIcon(R.drawable.ic_toc_white_24dp); /* toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { return false; } });*/ } private void initViews() { mDrawerLayout = (DrawerLayout) findViewById(R.id.id_drawerlayout); mActionBarDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.open, R.string.close); mActionBarDrawerToggle.syncState(); mDrawerLayout.setDrawerListener(mActionBarDrawerToggle); } }
  • 1
  • 2
  • 3

內(nèi)容區(qū)域的切換是通過(guò)Fragment hide和show實(shí)現(xiàn)的,畢竟如果用replace,如果Fragment的view結(jié)構(gòu)比較復(fù)雜,可能會(huì)有卡頓。當(dāng)然了,注意每個(gè)Fragment占據(jù)的內(nèi)存情況,如果內(nèi)存不足,可能需要改變實(shí)現(xiàn)方式。?
對(duì)于旋轉(zhuǎn)屏幕或者應(yīng)用長(zhǎng)時(shí)間置于后臺(tái),Activity重建的問(wèn)題,做了簡(jiǎn)單的處理。

對(duì)了,寫(xiě)布局的時(shí)候,可以盡可能的去考慮 Material design 的規(guī)范。

5、參考資料

    • Using the Material Theme
    • Android Support Library 22.1
    • Material design

? ? 本文轉(zhuǎn)自 一點(diǎn)點(diǎn)征服 ? 博客園博客,原文鏈接:http://www.cnblogs.com/ldq2016/p/7209719.html,如需轉(zhuǎn)載請(qǐng)自行聯(lián)系原作者
與50位技術(shù)專(zhuān)家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的Android 5.x Theme 与 ToolBar 实战的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。