如德芙般絲滑的ViewPager輪播

每次在地鐵上把玩別的應(yīng)用時(shí),看著別人家的輪播圖總是那么高大上贩耐,滑動(dòng)起來(lái)總是那么銜接完美弧腥,就像別人家的亞索和盲僧總是那么6。我不服潮太,solo管搪,于是當(dāng)即也手動(dòng)寫(xiě)一個(gè)虾攻。
Talk is cheap,show you the code

Talk is cheap,show you the effect:


giphy.gif

功能簡(jiǎn)介:

自定義的一個(gè)BannerView輪播器類(lèi),實(shí)現(xiàn)可滑動(dòng)指示點(diǎn)更鲁,可調(diào)節(jié)滑動(dòng)速度霎箍;

實(shí)現(xiàn)步驟:

1.BannerView引入布局:viewpager顯示輪播,linearlayout用于添加一排白色(未選中狀態(tài))小圓點(diǎn)澡为,view用于顯示紅色移動(dòng)小圓點(diǎn)漂坏。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="160dp"/>

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="14dp"
        android:layout_alignBottom="@id/viewpager">

        <LinearLayout
            android:id="@+id/dot_linear"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center_vertical"/>

        <View
            android:id="@+id/movedot"
            android:layout_width="6dp"
            android:layout_height="6dp"
            android:background="@drawable/dot_focus"/>
    </RelativeLayout>
</RelativeLayout>

2.viewpager的pageradapter類(lèi),每一項(xiàng)為imageview,實(shí)現(xiàn)多張切換媒至。

import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

public class BannerViewpagerAdapter extends PagerAdapter {
    private Context context;
    private int[] pics;

    public BannerViewpagerAdapter(Context context, int[] pics) {
        this.context = context;
        this.pics = pics;
    }

    @Override
    public int getCount() {
        return Integer.MAX_VALUE;
    }

    @Override
    public boolean isViewFromObject(View view, Object o) {
        return view == o;
    }

    /**
     * 相當(dāng)于baseadapter的getview
     * @param container
     * @param position
     * @return
     */
    @Override
    public Object instantiateItem(final ViewGroup container, final int position) {
        ImageView imageView = new ImageView(context);
        container.addView(imageView);
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        imageView.setImageResource(pics[position%pics.length]);

        return imageView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
    }
}
Bannerview類(lèi)中:

3.初始化顶别,包括初始化自動(dòng)輪播時(shí)間,圓點(diǎn)直徑塘慕,原點(diǎn)間隙筋夏,圓點(diǎn)之間圓心間距(算偏移量)。params1第一個(gè)點(diǎn)無(wú)左間距图呢,params2其余點(diǎn)有左間距。

    private Context context;
    private ViewPager viewPager;
    public PagerAdapter pagerAdapter;
    /** 自動(dòng)輪播時(shí)間間隔 */
    private final int DELAY_TIME = 4000;
    /** 頁(yè)面滾動(dòng)時(shí)間 */
    private final int SCROLL_TIME = 400;
    public LinearLayout linearLayout;
    private View bannerView;
    private int[] pics;
    private final int SCROLL_WHAT = 0;
    private LinearLayout.LayoutParams params1,params2;
    private View moveDot;
    /** 相鄰點(diǎn)圓心距離 */
    private int dotDistance;
    /** 圓點(diǎn)直徑 */
    private int dotDiameter;
    /** 圓之間間隔空隙 */
    private int dotSpace;

    public BannerView(Context context,int[] pics,PagerAdapter pagerAdapter,int layout){
        this.context = context;
        this.pics = pics;
        this.pagerAdapter = pagerAdapter;
        bannerView = LayoutInflater.from(context).inflate(layout,null);
        initView();
        event();
    }

    private void initView() {
        linearLayout = bannerView.findViewById(R.id.dot_linear);
        viewPager = bannerView.findViewById(R.id.viewpager);
        moveDot = bannerView.findViewById(R.id.movedot);
        //設(shè)置默認(rèn)viewpager當(dāng)前項(xiàng)
        viewPager.setCurrentItem(pics.length*Integer.MAX_VALUE/2);
        viewPager.setAdapter(pagerAdapter);
        pagerAdapter.notifyDataSetChanged();
        handler.sendEmptyMessageDelayed(SCROLL_WHAT, DELAY_TIME);

        dotSpace = dp2px(7);
        dotDiameter = dp2px(6);
        dotDistance = dotDiameter + dotSpace;

        params1 = new LinearLayout.LayoutParams(dotDiameter,dotDiameter);
        params1.leftMargin = 0;
        params2 = new LinearLayout.LayoutParams(dotDiameter,dotDiameter);
        params2.leftMargin = dotSpace;

        setViewPagerDuration();
        initDot();
    }

4.創(chuàng)建白色小圓點(diǎn)骗随,并添加到linearlayout中蛤织,第一個(gè)無(wú)左邊界,其余加入左邊界鸿染。

    /**
     * 創(chuàng)建小圓點(diǎn)
     */
    private void initDot(){
        View dot;
        for(int i=0;i<pics.length;i++){
            dot = new View(context);
            if(i == 0){
                dot.setLayoutParams(params1);
                dot.setBackgroundResource(R.drawable.dot_unfocus);
            }else{
                dot.setLayoutParams(params2);
                dot.setBackgroundResource(R.drawable.dot_unfocus);
            }
            linearLayout.addView(dot);
        }
    }

5.實(shí)現(xiàn)移動(dòng)小紅點(diǎn)指示點(diǎn):
pageChangeListener的onPageScrolled回調(diào)方法中指蚜,回調(diào)了positionOffset值,即滑動(dòng)當(dāng)前一頁(yè)的偏移百分比涨椒,范圍[0-1]摊鸡。可以根據(jù)實(shí)時(shí)設(shè)置小紅點(diǎn)params.leftMargin達(dá)到移動(dòng)效果蚕冬。那么當(dāng)前左邊距是多少呢免猾。比如滑動(dòng)到第二和第三頁(yè)之間,那么左邊距就是第一個(gè)點(diǎn)和第二個(gè)點(diǎn)的間隔dotDistance加上第二和第三點(diǎn)之間偏移乘以dotDistance囤热。


滑動(dòng)過(guò)程演示
    private void event(){
        viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) moveDot.getLayoutParams();

                //取余猎提,不然會(huì)一直往右移動(dòng)
                int dotPos = position % pics.length;
                //dotDistance圓心間距
                params.leftMargin = (int) (dotDistance * dotPos + dotDistance * positionOffset);
                moveDot.setLayoutParams(params);
            }

            @Override
            public void onPageSelected(int i) {
            }

            @Override
            public void onPageScrollStateChanged(int i) {
            }
        });

    }

6.實(shí)現(xiàn)自設(shè)置滾動(dòng)速度:
要設(shè)置滾動(dòng),滾動(dòng)事件來(lái)自于viewPager.setCurrentItem()旁蔼,那么順藤摸瓜锨苏,點(diǎn)進(jìn)setCurrentItem往上找:

    /**
     * Set the currently selected page. If the ViewPager has already been through its first
     * layout with its current adapter there will be a smooth animated transition between
     * the current item and the specified item.
     *
     * @param item Item index to select
     */
    public void setCurrentItem(int item) {
        mPopulatePending = false;
        setCurrentItemInternal(item, !mFirstLayout, false);
    }

實(shí)現(xiàn)來(lái)自setCurrentItemInternal(),點(diǎn)進(jìn)去繼續(xù)棺聊,來(lái)到了ViewPager類(lèi)中伞租。從setCurrentItemInternal() -> scrollToItem() -> smoothScrollTo(),里面最終是由mScroller.startScroll(sx, sy, dx, dy, duration)實(shí)現(xiàn)了滾動(dòng)限佩。所以我們要改變duration這個(gè)值葵诈,一不做二不休,采用偷梁換柱,自己Scroller類(lèi)驯击,覆寫(xiě)startScroll()方法烁兰,傳入自己的duration。mScroller是Viewpager的一個(gè)成員變量徊都,那就用反射替用自己scroller換掉原有的mScroller沪斟。

    void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
        setCurrentItemInternal(item, smoothScroll, always, 0);
    }

    void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
        if (mAdapter == null || mAdapter.getCount() <= 0) {
            setScrollingCacheEnabled(false);
            return;
        }
        if (!always && mCurItem == item && mItems.size() != 0) {
            setScrollingCacheEnabled(false);
            return;
        }

        if (item < 0) {
            item = 0;
        } else if (item >= mAdapter.getCount()) {
            item = mAdapter.getCount() - 1;
        }
        final int pageLimit = mOffscreenPageLimit;
        if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
            // We are doing a jump by more than one page.  To avoid
            // glitches, we want to keep all current pages in the view
            // until the scroll ends.
            for (int i = 0; i < mItems.size(); i++) {
                mItems.get(i).scrolling = true;
            }
        }
        final boolean dispatchSelected = mCurItem != item;

        if (mFirstLayout) {
            // We don't have any idea how big we are yet and shouldn't have any pages either.
            // Just set things up and let the pending layout handle things.
            mCurItem = item;
            if (dispatchSelected) {
                dispatchOnPageSelected(item);
            }
            requestLayout();
        } else {
            populate(item);
            scrollToItem(item, smoothScroll, velocity, dispatchSelected);
        }
    }

private void scrollToItem(int item, boolean smoothScroll, int velocity,
            boolean dispatchSelected) {
        final ItemInfo curInfo = infoForPosition(item);
        int destX = 0;
        if (curInfo != null) {
            final int width = getClientWidth();
            destX = (int) (width * Math.max(mFirstOffset,
                    Math.min(curInfo.offset, mLastOffset)));
        }
        if (smoothScroll) {
            smoothScrollTo(destX, 0, velocity);
            if (dispatchSelected) {
                dispatchOnPageSelected(item);
            }
        } else {
            if (dispatchSelected) {
                dispatchOnPageSelected(item);
            }
            completeScroll(false);
            scrollTo(destX, 0);
            pageScrolled(destX);
        }
    }

7.創(chuàng)建Scroller傳入duration覆寫(xiě)startScroll,通過(guò)反射取得mScroller屬性重設(shè)mScroller暇矫。

private void setViewPagerDuration(){
        try {
            Field field = ViewPager.class.getDeclaredField("mScroller");
            field.setAccessible(true);
            field.set(viewPager,getScroller(SCROLL_TIME));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private Scroller getScroller(final int smoothDuration){
        Scroller scroller = new Scroller(context,new AccelerateInterpolator()){
            @Override
            public void startScroll(int startX, int startY, int dx, int dy, int duration) {
                super.startScroll(startX, startY, dx, dy, smoothDuration);
            }
        };
        return scroller;
    }
好了主之,大功告成。完整Bannerview類(lèi):
blic class BannerView {
    private Context context;
    private ViewPager viewPager;
    public PagerAdapter pagerAdapter;
    /** 自動(dòng)輪播時(shí)間間隔 */
    private final int DELAY_TIME = 4000;
    /** 頁(yè)面滾動(dòng)時(shí)間 */
    private final int SCROLL_TIME = 400;
    public LinearLayout linearLayout;
    private View bannerView;
    private int[] pics;
    private final int SCROLL_WHAT = 0;
    private LinearLayout.LayoutParams params1,params2;
    private View moveDot;
    /** 相鄰點(diǎn)圓心距離 */
    private int dotDistance;
    /** 圓點(diǎn)直徑 */
    private int dotDiameter;
    /** 圓之間間隔空隙 */
    private int dotSpace;

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            viewPager.setCurrentItem(viewPager.getCurrentItem() + 1);
            handler.sendEmptyMessageDelayed(SCROLL_WHAT,DELAY_TIME);
        }
    };

    public BannerView(Context context,int[] pics,PagerAdapter pagerAdapter,int layout){
        this.context = context;
        this.pics = pics;
        this.pagerAdapter = pagerAdapter;
        bannerView = LayoutInflater.from(context).inflate(layout,null);
        initView();
        event();
    }

    private void initView() {
        linearLayout = bannerView.findViewById(R.id.dot_linear);
        viewPager = bannerView.findViewById(R.id.viewpager);
        moveDot = bannerView.findViewById(R.id.movedot);
        //設(shè)置默認(rèn)viewpager當(dāng)前項(xiàng)
        viewPager.setCurrentItem(pics.length*Integer.MAX_VALUE/2);
        viewPager.setAdapter(pagerAdapter);
        pagerAdapter.notifyDataSetChanged();
        handler.sendEmptyMessageDelayed(SCROLL_WHAT, DELAY_TIME);

        dotSpace = dp2px(7);
        dotDiameter = dp2px(6);
        dotDistance = dotDiameter + dotSpace;

        params1 = new LinearLayout.LayoutParams(dotDiameter,dotDiameter);
        params1.leftMargin = 0;
        params2 = new LinearLayout.LayoutParams(dotDiameter,dotDiameter);
        params2.leftMargin = dotSpace;

        setViewPagerDuration();
        initDot();
    }

    private void event(){
        viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) moveDot.getLayoutParams();

                //取余李根,不然會(huì)一直往右移動(dòng)
                int dotPos = position % pics.length;
                params.leftMargin = (int) (dotDistance * dotPos + dotDistance * positionOffset);
                moveDot.setLayoutParams(params);
            }

            @Override
            public void onPageSelected(int i) {
            }

            @Override
            public void onPageScrollStateChanged(int i) {
            }
        });

    }

    /**
     * 創(chuàng)建小圓點(diǎn)
     */
    private void initDot(){
        View dot;
        for(int i=0;i<pics.length;i++){
            dot = new View(context);
            if(i == 0){
                dot.setLayoutParams(params1);
                dot.setBackgroundResource(R.drawable.dot_unfocus);
            }else{
                dot.setLayoutParams(params2);
                dot.setBackgroundResource(R.drawable.dot_unfocus);
            }
            linearLayout.addView(dot);
        }
    }

    private void setViewPagerDuration(){
        try {
            Field field = ViewPager.class.getDeclaredField("mScroller");
            field.setAccessible(true);
            field.set(viewPager,getScroller(SCROLL_TIME));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private Scroller getScroller(final int smoothDuration){
        Scroller scroller = new Scroller(context,new AccelerateInterpolator()){
            @Override
            public void startScroll(int startX, int startY, int dx, int dy, int duration) {
                super.startScroll(startX, startY, dx, dy, smoothDuration);
            }
        };
        return scroller;
    }

    public View getBannerView(){
        return bannerView;
    }

    private int dp2px(int dp){
        return (int) (context.getResources().getDisplayMetrics().density*dp + 0.5);
    }

}

MainActivity類(lèi):

public class MainActivity extends AppCompatActivity {
    private BannerView bannerView;
    private BannerViewpagerAdapter pagerAdapter;
    private RelativeLayout rlBanner;
    private int[] pics = new int[]{R.mipmap.banner1,R.mipmap.banner2,R.mipmap.banner3,R.mipmap.banner4};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        init();
    }

    private void init() {
        rlBanner = (RelativeLayout) findViewById(R.id.rl_banner);
        pagerAdapter = new BannerViewpagerAdapter(getApplicationContext(),pics);
        bannerView = new BannerView(getApplicationContext(),pics,pagerAdapter,R.layout.customviewpager);
        //將bannerview添加到需引入控件即可
        rlBanner.addView(bannerView.getBannerView());
    }
}

重要的是想到找到滑動(dòng)偏移和設(shè)置時(shí)間的辦法槽奕,在實(shí)際做項(xiàng)目中,辦法總比困難多房轿。1000個(gè)讀者就有1000個(gè)林黛玉粤攒,那么1000個(gè)程序猿就有1000個(gè)方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末囱持,一起剝皮案震驚了整個(gè)濱河市夯接,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纷妆,老刑警劉巖盔几,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異掩幢,居然都是意外死亡逊拍,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)际邻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)芯丧,“玉大人,你說(shuō)我怎么就攤上這事枯怖∽⒄” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵度硝,是天一觀的道長(zhǎng)肿轨。 經(jīng)常有香客問(wèn)我,道長(zhǎng)蕊程,這世上最難降的妖魔是什么椒袍? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮藻茂,結(jié)果婚禮上驹暑,老公的妹妹穿的比我還像新娘玫恳。我一直安慰自己,他們只是感情好优俘,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布京办。 她就那樣靜靜地躺著,像睡著了一般帆焕。 火紅的嫁衣襯著肌膚如雪惭婿。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,255評(píng)論 1 308
  • 那天叶雹,我揣著相機(jī)與錄音财饥,去河邊找鬼。 笑死折晦,一個(gè)胖子當(dāng)著我的面吹牛钥星,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播满着,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼谦炒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了风喇?” 一聲冷哼從身側(cè)響起编饺,我...
    開(kāi)封第一講書(shū)人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎响驴,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體撕蔼,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡豁鲤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鲸沮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片琳骡。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖讼溺,靈堂內(nèi)的尸體忽然破棺而出楣号,到底是詐尸還是另有隱情,我是刑警寧澤怒坯,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布炫狱,位于F島的核電站,受9級(jí)特大地震影響剔猿,放射性物質(zhì)發(fā)生泄漏视译。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一归敬、第九天 我趴在偏房一處隱蔽的房頂上張望酷含。 院中可真熱鬧鄙早,春花似錦、人聲如沸椅亚。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)呀舔。三九已至弥虐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間别威,已是汗流浹背躯舔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留省古,地道東北人粥庄。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像豺妓,于是被迫代替她去往敵國(guó)和親惜互。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,283評(píng)論 25 707
  • 更多分享:http://www.cherylgood.cn Scorller類(lèi)官方介紹 This class en...
    Angels_安杰閱讀 963評(píng)論 0 3
  • 一琳拭、Android開(kāi)發(fā)初體驗(yàn) 二训堆、Android與MVC設(shè)計(jì)模式模型對(duì)象存儲(chǔ)著應(yīng)用的數(shù)據(jù)和業(yè)務(wù)邏輯。模型類(lèi)通常用來(lái)...
    為夢(mèng)想戰(zhàn)斗閱讀 889評(píng)論 0 3
  • 內(nèi)容是博主照著書(shū)敲出來(lái)的白嘁,博主碼字挺辛苦的坑鱼,轉(zhuǎn)載請(qǐng)注明出處,后序內(nèi)容陸續(xù)會(huì)碼出絮缅。 當(dāng)了解了Android坐標(biāo)系和觸...
    Blankj閱讀 6,649評(píng)論 3 61
  • 生活不只有眼前的茍且鲁沥,還有詩(shī)和遠(yuǎn)方。每天陷入具體的事情里耕魄,不斷輪迴画恰,漸漸地把自己當(dāng)成工具了,生活也不知道自己需要什...
    好彩妹閱讀 167評(píng)論 0 0