Programming     Travel Logs     Life Is Good     Surfing Online     About Me
To spark, often burst in hard stone.
-William Liebknecht
2018-07-17 18:02:21

Copy this link when reproducing:
http://www.casperlee.com/en/y/blog/203

To create a user-defined widget in Android application, there are 2 ways at least:
1. Extends a known widget and enriches its functions.
2. Extends the View class and creates the widget by drawing it totally.

For the TabHeaderView widget, I'll use the first method.

/Images/20171006/01.jpg

/Images/20171006/02.jpg

/Images/20171006/03.jpg

/Images/20171006/04.jpg

/Images/20171006/05.jpg

/Images/20171006/06.jpg

To create the TabHeaderView widget, it involves the following steps:

  • create the layout file for the widget
  • create a class which extends the LinearLayout class
  • enrich the functions

Here are the detailed steps:

1. Add a new layout file named "widget_tab_header_view_layout.xml" in the folder "app -> res -> layout", and put the following text in:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/MainLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >

    <ToggleButton
        android:id="@+id/ToggleButton0"
        android:layout_width="96dp"
        android:layout_height="wrap_content"
        android:checked="true"
        android:background="@drawable/widget_tab_header_button_background" />

    <ToggleButton
        android:id="@+id/ToggleButton1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/widget_tab_header_button_background" />

    <ToggleButton
        android:id="@+id/ToggleButton2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/widget_tab_header_button_background" />

    <ToggleButton
        android:id="@+id/ToggleButton3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/widget_tab_header_button_background" />

    <ToggleButton
        android:id="@+id/ToggleButton4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/widget_tab_header_button_background" />

    <ToggleButton
        android:id="@+id/ToggleButton5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/widget_tab_header_button_background" />
</LinearLayout>

2. As you may have noticed, I've put several Toggle Buttons into the layout. Since they are buttons, they should display differently when user pressing it or ticking it. So I need to create a background definition file for them.

Add a new XML file named "widget_tab_header_button_background.xml" in the folder "app -> res -> drawable", and put the following text in:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    
    <item android:state_checked="true" android:state_pressed="true">
        <shape>
			<gradient android:angle="270" android:endColor="#ffc2b7" android:startColor="#ffc2b7" />
			<stroke android:width="2dp" android:color="#dcdcdc" />
			<corners android:radius="2dp" /> 
			<padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" />            
        </shape>
    </item>
    <item android:state_checked="true">
        <shape>
			<gradient android:angle="270" android:endColor="#ff9d77" android:startColor="#ff9d77" />
			<stroke android:width="2dp" android:color="#fad3cf" />
			<corners android:radius="2dp" />
			<padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" />            
        </shape>
    </item>
    <item>
        <shape>
			<gradient android:angle="270" android:endColor="#ff9d77" android:startColor="#ff9d77" />
			<stroke android:width="2dp" android:color="#FF000000" />
			<corners android:radius="2dp" />
			<padding android:bottom="10dp" android:left="10dp" android:right="10dp" android:top="10dp" />            
        </shape>
    </item>
    
</selector>

3. Add a new class named "TabHeaderView" in the folder "app -> java -> com.casperlee.personalexpense -> widgets", and put the following code in:

package com.casperlee.personalexpense.widgets;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ToggleButton;

import com.casperlee.personalexpense.R;

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

public class TabHeaderView extends LinearLayout {

    // Fields
    private static final int MAX_TAB_COUNT = 6;
    private static final int DEFAULT_TAB_COUNT = 2;
    private Layout ui;
    private Context context;
    private OnTabChangeListener listener;

    // Entrances
    public TabHeaderView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;

        this.initFields();
        this.createLayout(attrs);
        this.initLayout();
        this.setListeners();
    }

    // Interfaces
    public interface OnTabChangeListener {

        public abstract boolean onTabChanging(int tabIndex);
        public abstract void onTabChanged(int tabIndex);
    }

    // Properties
    private int tabCount;
    public int getTabCount() {
        return tabCount;
    }
    public void setTabCount(int tabCount) {

        this.tabCount = tabCount;
        for (int i = 0; i < MAX_TAB_COUNT; i++) {

            if (i < tabCount) {

                this.ui.toggleButtons.get(i).setVisibility(View.VISIBLE);

            } else {

                this.ui.toggleButtons.get(i).setVisibility(View.GONE);
            }
        }

        this.invalidate();
    }

    // Public methods
    public void setTabChangeListener(OnTabChangeListener l) {

        this.listener = l;
    }
    public void setTabCaption(int tabIndex, String caption) {

        if (tabIndex < 0 || tabIndex >= MAX_TAB_COUNT) {

            return;
        }

        this.ui.toggleButtons.get(tabIndex).setTextOff(caption);
        this.ui.toggleButtons.get(tabIndex).setTextOn(caption);
        this.invalidate();
    }

    // Private methods
    private void initFields() {

        this.tabCount = DEFAULT_TAB_COUNT;
    }
    private void createLayout(AttributeSet attrs) {

        View v = inflate(this.context,
                R.layout.widget_tab_header_view_layout, null);
        v.setLayoutParams(new LayoutParams(this.context, attrs));
        this.addView(v, 0);
        this.ui = new Layout();
    }
    private void initLayout() {

        this.setTabCount(this.tabCount);
    }
    private void setListeners() {

        this.setViewClickListeners();
    }
    private void setViewClickListeners() {

        OnMyViewClickListener l = new OnMyViewClickListener();
        for (int i = 0; i < MAX_TAB_COUNT; i++) {

            this.ui.toggleButtons.get(i).setOnClickListener(l);
        }
    }

    // Classes
    private class Layout {

        private ToggleButton ToggleButton0 = null;
        private ToggleButton ToggleButton1 = null;
        private ToggleButton ToggleButton2 = null;
        private ToggleButton ToggleButton3 = null;
        private ToggleButton ToggleButton4 = null;
        private ToggleButton ToggleButton5 = null;
        private List<ToggleButton> toggleButtons = null;

        public Layout() {

            this.initialize();
        }

        private void initialize() {

            this.ToggleButton0 = (ToggleButton)TabHeaderView.this.findViewById(R.id.ToggleButton0);
            this.ToggleButton1 = (ToggleButton)TabHeaderView.this.findViewById(R.id.ToggleButton1);
            this.ToggleButton2 = (ToggleButton)TabHeaderView.this.findViewById(R.id.ToggleButton2);
            this.ToggleButton3 = (ToggleButton)TabHeaderView.this.findViewById(R.id.ToggleButton3);
            this.ToggleButton4 = (ToggleButton)TabHeaderView.this.findViewById(R.id.ToggleButton4);
            this.ToggleButton5 = (ToggleButton)TabHeaderView.this.findViewById(R.id.ToggleButton5);
            this.toggleButtons = new ArrayList<ToggleButton>();
            this.toggleButtons.add(this.ToggleButton0);
            this.toggleButtons.add(this.ToggleButton1);
            this.toggleButtons.add(this.ToggleButton2);
            this.toggleButtons.add(this.ToggleButton3);
            this.toggleButtons.add(this.ToggleButton4);
            this.toggleButtons.add(this.ToggleButton5);
        }
    }

    private class OnMyViewClickListener implements View.OnClickListener {

        @Override
        public void onClick(View v) {

            for (int i = 0; i < MAX_TAB_COUNT; i++) {

                ToggleButton btn = TabHeaderView.this.ui.toggleButtons.get(i);
                if (v == btn) {

                    if (!btn.isChecked()) {

                        btn.setChecked(true);

                    } else {

                        if (listener != null) {

                            listener.onTabChanged(i);
                        }
                    }

                } else {

                    btn.setChecked(false);
                }
            }
        }
    }
}

4. Done!