一個可以自適應換行的標籤列表控件

在我剛學Android的時候,看到b站的手機端app裏,在顯示搜索熱詞的時候有這樣一個效果:


                                                 

我當時覺得很神奇,直到後來某一天我突然想明白是怎麼回事了。


這次就帶來這樣一個控件:可以自定義添加標籤,並且新添加的標籤可以根據其長度,如果當前行放不下的話自動換到下一行。


首先說一下實現思路:我們可以把整個東西看成是一個縱向排布的LinearLayout,裏面的每一行內容就是一個橫向排布的子

LinearLayout裏裝若干個TextView,所謂的”自動換行“事實上就是判斷新增加的TextView在放入後是否會超出其右邊界,如

超出則新建一個橫向排布的子LinearLayout,即新的一行,把TextView放入其中。現在的問題就變成有沒有辦法知道一個已知內

容的TextView的寬度?當然有辦法,Paint類下的measureText方法提供了這個功能。


原理講完,下面上控件本體:

SelfAdaptionColunmLayout.java:

public class SelfAdaptionColumnLayout extends LinearLayout {
    // 圖標位於標籤左邊
    public static final int ICON_LEFT = 0x001;

    // 圖標位於標籤右邊
    public static final int ICON_RIGHT = 0x002;


    private static final String KEY_TEXTVIEW = "KAY_TEXTVIEW";
    private static final String KEY_TEXTITEM = "KEY_TEXTITEM";

    private Context context;
    private ArrayList<HashMap> list;

    private int layoutWidth;

    // 行間距
    private int lineMargin = 10;

    // 列間距,即同一行相鄰標籤之間的距離
    private int columnMargin = 10;

    // 默認標籤文字顏色
    private int defaultColor = Color.parseColor("#000000");

    // 默認標籤文字大小
    private int defaultSize = 16;

    // 標籤內的圖標位置
    private int iconGravity = ICON_LEFT;

    // 標籤內的圖標距離文字的距離
    private int iconPadding = 5;

    private int currentLength = 0;

    // 標籤點擊回調
    private OnItemClickListener listener;

    public SelfAdaptionColumnLayout(Context context) {
        this(context, null);
    }

    public SelfAdaptionColumnLayout(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public SelfAdaptionColumnLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    /**
     * 初始化方法
     *
     * @param context
     */
    private void init(Context context) {
        this.context = context;
        list = new ArrayList<>();
        setOrientation(VERTICAL);
        setGravity(Gravity.LEFT);
        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                getViewTreeObserver().removeGlobalOnLayoutListener(this);
                layoutWidth = getWidth();
                notifyDataSetChanged();
            }
        });
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() != 0) {
            throw new RuntimeException("layout should not have any child");
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        layoutWidth = getMeasuredWidth();
    }

    /**
     * 繪製標籤
     *
     * @param position
     * @param textview
     * @param item
     */
    private void drawText(final int position, TextView textview, TextItem item) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextSize(item.getTextSize() != 0 ? item.getTextSize() : defaultSize);
        if (item.getIcon() != null && item.isShowIcon()) {
            item.getIcon().setBounds(0, 0, dp2px(item.getIconSize()), dp2px(item.getIconSize()));
        }
        if (getChildCount() == 0) {
            LinearLayout parent = new LinearLayout(context);
            parent.setGravity(Gravity.CENTER_VERTICAL);
            parent.setOrientation(HORIZONTAL);
            textview.setText(item.getText());
            textview.setGravity(Gravity.CENTER_VERTICAL);
            textview.setSingleLine(true);
            textview.setTextSize(item.getTextSize() != 0 ? item.getTextSize() : defaultSize);
            textview.setTextColor(item.getTextColor() != 0 ? item.getTextColor() : defaultColor);
            textview.setPadding(dp2px(item.getTextPaddingLeftRight()), dp2px(item.getTextPaddingTopBottom()), dp2px(item.getTextPaddingLeftRight()), dp2px(item.getTextPaddingTopBottom()));
            textview.setCompoundDrawables(item.getIcon() != null && item.isShowIcon() && iconGravity == ICON_LEFT ? item.getIcon() : null, null,
                    item.getIcon() != null && item.isShowIcon() && iconGravity == ICON_RIGHT ? item.getIcon() : null, null);
            textview.setCompoundDrawablePadding(dp2px(iconPadding));
            textview.setBackgroundDrawable(item.getTextBackground());
            textview.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (listener != null) {
                        listener.onItemClick(position, ((TextItem) list.get(position).get(KEY_TEXTITEM)).getText());
                    }
                }
            });
            if (textview.getParent() != null) {
                ((LinearLayout) textview.getParent()).removeView(textview);
            }
            parent.addView(textview, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            addView(parent, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            if (item.getIcon() != null && item.isShowIcon()) {
                currentLength = dp2px(iconPadding) + 2 * dp2px(item.getTextPaddingLeftRight()) + dp2px(paint.measureText(item.getText())) + dp2px(item.getIconSize());
            } else {
                currentLength = 2 * dp2px(item.getTextPaddingLeftRight()) + dp2px(paint.measureText(item.getText()));
            }
        } else {
            LinearLayout parent = (LinearLayout) getChildAt(getChildCount() - 1);
            boolean isNeedWrap;
            if (item.getIcon() != null && item.isShowIcon()) {
                isNeedWrap = currentLength + dp2px(columnMargin) + dp2px(iconPadding) + 2 * dp2px(item.getTextPaddingLeftRight())
                        + dp2px(paint.measureText(item.getText())) + dp2px(item.getIconSize()) + getPaddingRight() > layoutWidth;
            } else {
                isNeedWrap = currentLength + dp2px(columnMargin) + 2 * dp2px(item.getTextPaddingLeftRight())
                        + dp2px(paint.measureText(item.getText())) + getPaddingRight() > layoutWidth;
            }
            if (isNeedWrap) {
                parent = new LinearLayout(context);
                parent.setGravity(Gravity.CENTER_VERTICAL);
                parent.setOrientation(HORIZONTAL);
                textview.setText(item.getText());
                textview.setGravity(Gravity.CENTER);
                textview.setSingleLine(true);
                textview.setTextSize(item.getTextSize() != 0 ? item.getTextSize() : defaultSize);
                textview.setTextColor(item.getTextColor() != 0 ? item.getTextColor() : defaultColor);
                textview.setPadding(dp2px(item.getTextPaddingLeftRight()), dp2px(item.getTextPaddingTopBottom()), dp2px(item.getTextPaddingLeftRight()), dp2px(item.getTextPaddingTopBottom()));
                textview.setCompoundDrawables(item.getIcon() != null && item.isShowIcon() && iconGravity == ICON_LEFT ? item.getIcon() : null, null,
                        item.getIcon() != null && item.isShowIcon() && iconGravity == ICON_RIGHT ? item.getIcon() : null, null);
                textview.setCompoundDrawablePadding(dp2px(iconPadding));
                textview.setBackgroundDrawable(item.getTextBackground());
                textview.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (listener != null) {
                            listener.onItemClick(position, ((TextItem) list.get(position).get(KEY_TEXTITEM)).getText());
                        }
                    }
                });
                if (textview.getParent() != null) {
                    ((LinearLayout) textview.getParent()).removeView(textview);
                }
                parent.addView(textview, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                addView(parent, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                ((LinearLayout.LayoutParams) parent.getLayoutParams()).setMargins(0, dp2px(lineMargin), 0, 0);
                if (item.getIcon() != null && item.isShowIcon()) {
                    currentLength = dp2px(iconPadding) + 2 * dp2px(item.getTextPaddingLeftRight()) + dp2px(paint.measureText(item.getText())) + dp2px(item.getIconSize());
                } else {
                    currentLength = 2 * dp2px(item.getTextPaddingLeftRight()) + dp2px(paint.measureText(item.getText()));
                }
            } else {
                textview.setText(item.getText());
                textview.setGravity(Gravity.CENTER);
                textview.setSingleLine(true);
                textview.setTextSize(item.getTextSize() != 0 ? item.getTextSize() : defaultSize);
                textview.setTextColor(item.getTextColor() != 0 ? item.getTextColor() : defaultColor);
                textview.setPadding(dp2px(item.getTextPaddingLeftRight()), dp2px(item.getTextPaddingTopBottom()), dp2px(item.getTextPaddingLeftRight()), dp2px(item.getTextPaddingTopBottom()));
                textview.setCompoundDrawables(item.getIcon() != null && item.isShowIcon() && iconGravity == ICON_LEFT ? item.getIcon() : null, null,
                        item.getIcon() != null && item.isShowIcon() && iconGravity == ICON_RIGHT ? item.getIcon() : null, null);
                textview.setCompoundDrawablePadding(dp2px(iconPadding));
                textview.setBackgroundDrawable(item.getTextBackground());
                textview.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (listener != null) {
                            listener.onItemClick(position, ((TextItem) list.get(position).get(KEY_TEXTITEM)).getText());
                        }
                    }
                });
                if (textview.getParent() != null) {
                    ((LinearLayout) textview.getParent()).removeView(textview);
                }
                parent.addView(textview, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                ((LinearLayout.LayoutParams) textview.getLayoutParams()).setMargins(dp2px(columnMargin), 0, 0, 0);
                if (item.getIcon() != null && item.isShowIcon()) {
                    currentLength += dp2px(columnMargin) + dp2px(iconPadding) + 2 * dp2px(item.getTextPaddingLeftRight()) + dp2px(paint.measureText(item.getText())) + dp2px(item.getIconSize());
                } else {
                    currentLength += dp2px(columnMargin) + 2 * dp2px(item.getTextPaddingLeftRight()) + dp2px(paint.measureText(item.getText()));
                }
            }
        }
    }

    /**
     * 添加一個標籤
     * 添加後必須調用notifyDataSetChanged()方法纔會生效
     *
     * @param text 標籤文字
     */
    public void addItem(@NonNull String text) {
        HashMap temp = new HashMap();
        temp.put(KEY_TEXTVIEW, new TextView(context));
        temp.put(KEY_TEXTITEM, new TextItem(text));
        list.add(temp);
    }

    /**
     * 添加一個標籤
     * 添加後必須調用notifyDataSetChanged()方法纔會生效
     *
     * @param text                 標籤文字
     * @param textSize             標籤文字大小,單位sp
     * @param textColor            標籤文字顏色
     * @param textPaddingTopBottom 標籤文字距離上下邊的距離,單位dp
     * @param textPaddingLeftRight 標籤文字距離左右邊的距離,單位dp
     * @param textBackground       標籤背景
     */
    public void addItem(@NonNull String text, int textSize, int textColor, int textPaddingTopBottom, int textPaddingLeftRight, Drawable textBackground) {
        HashMap temp = new HashMap();
        temp.put(KEY_TEXTVIEW, new TextView(context));
        temp.put(KEY_TEXTITEM, new TextItem(text, textSize, textColor, textPaddingTopBottom, textPaddingLeftRight, textBackground));
        list.add(temp);
    }

    /**
     * 添加一個標籤
     * 添加後必須調用notifyDataSetChanged()方法纔會生效
     *
     * @param icon                 標籤圖標
     * @param iconSize             標籤圖標大小,即正方形圖標的邊長,單位dp
     * @param isShowIcon           標籤圖標是否顯示
     * @param text                 標籤文字
     * @param textSize             標籤文字大小,單位sp
     * @param textColor            標籤文字顏色
     * @param textPaddingTopBottom 標籤文字距離上下邊的距離,單位dp
     * @param textPaddingLeftRight 標籤文字距離左右邊的距離,單位dp
     * @param textBackground       標籤背景
     */
    public void addItem(Drawable icon, int iconSize, boolean isShowIcon, @NonNull String text, int textSize, int textColor, int textPaddingTopBottom, int textPaddingLeftRight, Drawable textBackground) {
        HashMap temp = new HashMap();
        temp.put(KEY_TEXTVIEW, new TextView(context));
        temp.put(KEY_TEXTITEM, new TextItem(icon, iconSize, isShowIcon, text, textSize, textColor, textPaddingTopBottom, textPaddingLeftRight, textBackground));
        list.add(temp);
    }

    /**
     * 刪除指定指定標籤
     * 刪除後必須調用notifyDataSetChanged()方法纔會生效
     *
     * @param position 標籤座標
     */
    public void removeItem(int position) {
        list.remove(position);
    }

    /**
     * 獲取指定標籤實體
     *
     * @param position 標籤序號
     * @return 標籤實體
     */
    public TextItem getItem(int position) {
        return (TextItem) list.get(position).get(KEY_TEXTITEM);
    }

    /**
     * 修改指定標籤
     * 修改後必須調用notifyDataSetChanged()方法纔會生效
     *
     * @param position 標籤序號
     * @param item     標籤實體
     */
    public void setItem(int position, TextItem item) {
        list.get(position).put(KEY_TEXTITEM, item);
    }

    /**
     * 清除所有標籤
     * 清除後必須調用notifyDataSetChanged()方法纔會生效
     */
    public void clearAllItem() {
        list.clear();
    }

    /**
     * 獲取標籤總數
     *
     * @return 標籤總數
     */
    public int getItemCount() {
        return list.size();
    }

    /**
     * 設置行間距
     *
     * @param lineMargin 行間距,單位dp
     */
    public void setLineMargin(int lineMargin) {
        this.lineMargin = lineMargin;
    }

    /**
     * 設置列間距,即同一行相鄰標籤之間的距離
     *
     * @param columnMargin 列間距,單位dp
     */
    public void setColumnMargin(int columnMargin) {
        this.columnMargin = columnMargin;
    }

    /**
     * 設置默認標籤文字顏色
     *
     * @param defaultColor,顏色值
     */
    public void setDefaultColor(int defaultColor) {
        this.defaultColor = defaultColor;
    }

    /**
     * 設置默認標籤文字大小
     *
     * @param defaultSize,大小值,單位sp
     */
    public void setDefaultSize(int defaultSize) {
        this.defaultSize = defaultSize;
    }

    /**
     * 設置標籤內的圖標位置
     *
     * @param iconGravity 圖標位置,可選項:ICON_LEFT、ICON_RIGHT
     */
    public void setIconGravity(int iconGravity) {
        this.iconGravity = iconGravity;
    }

    /**
     * 標籤內圖標距離文字的距離
     *
     * @param iconPadding 距離值,單位dp
     */
    public void setIconPadding(int iconPadding) {
        this.iconPadding = iconPadding;
    }

    /**
     * 更新視圖
     * 在對標籤增、刪、改操作後必須調用此方法纔會生效
     */
    public void notifyDataSetChanged() {
        if (layoutWidth != 0) {
            currentLength = 0;
            removeAllViews();
            for (int i = 0; i < list.size(); i++) {
                drawText(i, (TextView) list.get(i).get(KEY_TEXTVIEW), (TextItem) list.get(i).get(KEY_TEXTITEM));
            }
        }
    }

    /**
     * 標籤實體
     */
    class TextItem {
        // 標籤內圖標
        private Drawable icon;

        // 標籤內圖標的大小,即正方形圖標的邊長,單位dp
        private int iconSize;

        // 標籤內圖標是否顯示
        private boolean isShowIcon;

        // 標籤文字
        private String text;

        // 標籤文字大小,單位sp
        private int textSize;

        // 標籤文字顏色
        private int textColor;

        // 標籤文字距離上下邊的距離,單位dp
        private int textPaddingTopBottom;

        // 標籤文字距離左右邊的距離,單位dp
        private int textPaddingLeftRight;

        // 標籤背景
        private Drawable textBackground;

        public TextItem(@NonNull String text) {
            this.text = text;
        }

        public TextItem(@NonNull String text, int textSize, int textColor, int textPaddingTopBottom, int textPaddingLeftRight, Drawable textBackground) {
            this.text = text;
            this.textSize = textSize;
            this.textColor = textColor;
            this.textPaddingTopBottom = textPaddingTopBottom;
            this.textPaddingLeftRight = textPaddingLeftRight;
            this.textBackground = textBackground;
        }

        public TextItem(Drawable icon, int iconSize, boolean isShowIcon, @NonNull String text, int textSize, int textColor, int textPaddingTopBottom, int textPaddingLeftRight, Drawable textBackground) {
            this.icon = icon;
            this.iconSize = iconSize;
            this.text = text;
            this.isShowIcon = isShowIcon;
            this.textSize = textSize;
            this.textColor = textColor;
            this.textPaddingTopBottom = textPaddingTopBottom;
            this.textPaddingLeftRight = textPaddingLeftRight;
            this.textBackground = textBackground;
        }

        public Drawable getIcon() {
            return icon;
        }

        public void setIcon(Drawable icon) {
            this.icon = icon;
        }

        public int getIconSize() {
            return iconSize;
        }

        public void setIconSize(int iconSize) {
            this.iconSize = iconSize;
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }

        public boolean isShowIcon() {
            return isShowIcon;
        }

        public void setIsShowIcon(boolean isShowIcon) {
            this.isShowIcon = isShowIcon;
        }

        public int getTextSize() {
            return textSize;
        }

        public void setTextSize(int textSize) {
            this.textSize = textSize;
        }

        public int getTextColor() {
            return textColor;
        }

        public int getTextPaddingTopBottom() {
            return textPaddingTopBottom;
        }

        public void setTextPaddingTopBottom(int textPaddingTopBottom) {
            this.textPaddingTopBottom = textPaddingTopBottom;
        }

        public int getTextPaddingLeftRight() {
            return textPaddingLeftRight;
        }

        public void setTextPaddingLeftRight(int textPaddingLeftRight) {
            this.textPaddingLeftRight = textPaddingLeftRight;
        }

        public void setTextColor(int textColor) {
            this.textColor = textColor;
        }

        public Drawable getTextBackground() {
            return textBackground;
        }

        public void setTextBackground(Drawable textBackground) {
            this.textBackground = textBackground;
        }
    }

    private int sp2px(float spValue) {
        float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }

    private int dp2px(float dipValue) {
        float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }

    public interface OnItemClickListener {
        void onItemClick(int position, String text);
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        this.listener = listener;
    }
}

只有一個文件,複製進項目就能用了,上MainActivity.java和activity_main.xml:

MainActivity.java:

public class MainActivity extends Activity {
    private EditText editText;
    private Button button;
    private SelfAdaptionColumnLayout layout;

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

    private void initData() {

    }

    private void initView() {
        editText = (EditText) findViewById(R.id.edittext);
        button = (Button) findViewById(R.id.add_button);
        layout = (SelfAdaptionColumnLayout) findViewById(R.id.layout);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                layout.addItem(editText.getText().toString());
                layout.notifyDataSetChanged();
                editText.setText("");
            }
        });

    }
}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.min.selfadaptioncolumnlayout.SelfAdaptionColumnLayout
        android:id="@+id/layout"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_margin="10dp"
        android:layout_weight="1"
        android:orientation="vertical" />

    <EditText
        android:id="@+id/edittext"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_margin="10dp" />

    <Button
        android:id="@+id/add_button"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_margin="10dp"
        android:text="add" />

</LinearLayout>

代碼很簡單,唯一需要注意的是在執行完addItem方法添加一個標籤後,一定要執行notifyDataSetChanged刷新一下,添加才

會生效,這就跟ListView裏改動數據後需要執行adapter. notifyDataSetChanged()一樣。


運行看一下效果:

                                                                 

這裏說明一下,控件提供的添加標籤方法addItem有三種調用方式,即:

public void addItem(@NonNull String text)

public void addItem(@NonNull String text, int textSize, int textColor, int textPaddingTopBottom, int textPaddingLeftRight, Drawable textBackground) 

public void addItem(Drawable icon, int iconSize, boolean isShowIcon, @NonNull String text, int textSize, int textColor, int textPaddingTopBottom, int textPaddingLeftRight, Drawable textBackground)

以上的例子只是調用了第一種也是最簡單的一種,下面用第二種方法添加標籤,先定義幾個資源文件:

backgroud_up.xml:

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

    <corners android:radius="20dp" />

    <stroke
        android:width="1dp"
        android:color="#dddddd" />

    <solid android:color="#ffffff" />
</shape>

backgroud_down.xml:

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

    <corners android:radius="20dp" />

    <stroke
        android:width="1dp"
        android:color="#888888" />

    <solid android:color="#ffffff" />
</shape>
background_selector.xml:

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

    <item android:drawable="@drawable/backgroud_up" android:state_pressed="false"></item>
    <item android:drawable="@drawable/backgroud_down" android:state_pressed="true"></item>

</selector>

然後修改頁面中按鈕的點擊事件:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        layout.addItem(editText.getText().toString(), 20, Color.parseColor("#333333"),
                5, 15, getResources().getDrawable(R.drawable.background_selector));
        layout.notifyDataSetChanged();
        editText.setText("");
    }
});

完成,看一下效果:

                                                                
      

第三種調用方法支持在標籤左側或右側添加一個小圖標,以下也做一個示範:


先添加一個圖標:

hot.png:


然後繼續修改按鈕點擊事件:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (layout.getItemCount() < 3) {
            layout.addItem(getResources().getDrawable(R.drawable.hot), 20, true, editText.getText().toString(),
                    20, Color.parseColor("#3F51B5"), 5, 15,                           getResources().getDrawable(R.drawable.background_selector));
        } else {
            layout.addItem(editText.getText().toString(), 16, Color.parseColor("#333333"),
                    3, 10, getResources().getDrawable(R.drawable.background_selector));
        }
        layout.notifyDataSetChanged();
        editText.setText("");
    }
});

再次運行:

                                                                 

如果需要監聽標籤的點擊事件,只需要設置setOnItemClickListener即可,很簡單,不再累述。控件提供的更多方法在源碼裏都

有註釋,大家可以自己試一試。


最後附上源碼地址:點擊打開鏈接

 

這次的內容就到這裏,我們下次再見。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章