1. 统一重复组件风格

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# styles.xml
<resources>

    <style name="FormRadioButton" parent="android:Widget.CompoundButton.RadioButton">
        <item name="android:minHeight">@dimen/buttonHeight</item>
        <item name="android:button">@null</item>
        <item name="android:background">@drawable/background_radio</item>
        <item name="android:gravity">center</item>
    </style>

</resources>


# activity_layout.xml
<...>

<RadioGroup
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <RadioButton
            style="@style/FormRadioButton"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="One"/>
        <RadioButton
            style="@style/FormRadioButton"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Two"/>
        <RadioButton
            style="@style/FormRadioButton"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Three"/>
</RadioGroup>

</...>

2. 系统界面元素触发

  • View.setSystemUiVisibility()
  • View.getSystemUiVisibility()

3. 自定义View

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
public class BullsEyeView extends View {
    private Paint mPaint;
    private Point mCenter;
    private float mRadius;
    
    /*
     * Java Constructor
     */
    public BullsEyeView(Context context) {
        this(context, null);
    }
    
    /*
     * XML Constructor
     */
    public BullsEyeView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    
    /*
     * XML Constructor with Style
     */
    public BullsEyeView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        //在这里可以做自定义View的一些初始化配置

        //例如,创建一个可绘画的刷子
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        //设置刷子的风格
        mPaint.setStyle(Style.FILL);

        //创建绘画的中心点
        mCenter = new Point();
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width, height;

        //设置大小
        int contentWidth = 200;
        int contentHeight = 200;
        width = getMeasurement(widthMeasureSpec, contentWidth);
        height = getMeasurement(heightMeasureSpec, contentHeight);

        //必须调用此方法
        setMeasuredDimension(width, height);
    }

    /*
     * 辅助设置大小的方法
     */
    private int getMeasurement(int measureSpec, int contentSize) {
        int specSize = MeasureSpec.getSize(measureSpec);
        switch (MeasureSpec.getMode(measureSpec)) {
            case MeasureSpec.AT_MOST:
                return Math.min(specSize, contentSize);
            case MeasureSpec.UNSPECIFIED:
                return contentSize;
            case MeasureSpec.EXACTLY:
                return specSize;
            default:
                return 0;
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        if (w != oldw || h != oldh) {
            //如果大小有变,要重置中心点以及半径
            mCenter.x = w / 2;
            mCenter.y = h / 2;
            mRadius = Math.min(mCenter.x, mCenter.y);
        } 
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //绘制圆形
        // 用不同颜色从最小到最大绘制
        mPaint.setColor(Color.RED);
        canvas.drawCircle(mCenter.x, mCenter.y, mRadius, mPaint);
        mPaint.setColor(Color.WHITE);
        canvas.drawCircle(mCenter.x, mCenter.y, mRadius * 0.8f, mPaint);
        mPaint.setColor(Color.BLUE);
        canvas.drawCircle(mCenter.x, mCenter.y, mRadius * 0.6f, mPaint);
        mPaint.setColor(Color.WHITE);
        canvas.drawCircle(mCenter.x, mCenter.y, mRadius * 0.4f, mPaint);
        mPaint.setColor(Color.RED);
        canvas.drawCircle(mCenter.x, mCenter.y, mRadius * 0.2f, mPaint); 
    }
}

4. View动画

  • View.animate()
  • ObjectAnimator
  • AnimatorSet

5. Layout动画

  • removeView(View)
  • addView(View, LayoutParams)

layout.xml需要添加:android:animateLayoutChanges="true"

  • Layout.setLayoutTransition(LayoutTransition)

6. 方位调整

  • 垂直:res/layout-port
  • 水平:res/layout-land

7. AdapterView空数据时的处理

  • AdapterView.setEmptyView()

8. 自定义ListView

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# res/drawable/row_background_default.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <gradient
        android:startColor="#EFEFEF"
        android:endColor="#989898"
        android:type="linear"
        android:angle="270" /> 
</shape>

# res/drawable/row_background_pressed.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <gradient
        android:startColor="#0B8CF2"
        android:endColor="#0661E5"
        android:type="linear"
        android:angle="270" /> 
</shape>

# res/drawable/row_background.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:drawable="@drawable/row_background_pressed"/>
    <item android:drawable="@drawable/row_background_default"/>
</selector>


# res/layout/custom_row.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="wrap_content"
    android:padding="10dip"
    android:background="@drawable/row_background">
    <TextView
        android:id="@+id/line1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"    />
</LinearLayout>

9. 带字段头的ListView

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# Item
public class SectionItem<T> {
    private String mTitle;
    private T[] mItems;
    public SectionItem(String title, T[] items) {
        if (title == null) title = "";
        mTitle = title;
        mItems = items;
    }
    public String getTitle() {
        return mTitle;
    }
    public T getItem(int position) {
        return mItems[position];
    }
    public int getCount() {
        //包括额外的Item以及表头
        return (mItems == null ? 1 : 1 + mItems.length);
    }
    
    @Override
    public boolean equals(Object object) {
        //Two sections are equal if they have the same title
        if (object != null && object instanceof SectionItem) {
            return ((SectionItem) object).getTitle().equals(mTitle);
        }
        return false;
    }
}

# Adapter
public abstract class SimpleSectionsAdapter<T> extends BaseAdapter implements AdapterView.OnItemClickListener {
    /* 常量区分表头还是表身 */
    private static final int TYPE_HEADER = 0;
    private static final int TYPE_ITEM = 1;

    private LayoutInflater mLayoutInflater;
    private int mHeaderResource;
    private int mItemResource;

    /* 数据列表 */
    private List<SectionItem<T>> mSections;

    /* 列表分组 */
    private SparseArray<SectionItem<T>> mKeyedSections;
    
    public SimpleSectionsAdapter(ListView parent, int headerResId, int itemResId) {
        mLayoutInflater = LayoutInflater.from(parent.getContext());
        mHeaderResource = headerResId;
        mItemResource = itemResId;
        
        //初始化数据
        mSections = new ArrayList<SectionItem<T>>();
        mKeyedSections = new SparseArray<SectionItem<T>>();

        //添加监听器
        parent.setOnItemClickListener(this);
    }

    /*
     * 添加新的数据
     * 或者更新
     */
    public void addSection(String title, T[] items) {
        SectionItem<T> sectionItem = new SectionItem<T>(title, items);
        //添加数据, 更新已有的数据
        int currentIndex = mSections.indexOf(sectionItem);
        if (currentIndex >= 0) {
            mSections.remove(sectionItem);
            mSections.add(currentIndex, sectionItem);
        } else {
            mSections.add(sectionItem);
        }
        //排序
        reorderSections();
        //更新试图
        notifyDataSetChanged();
    }

    /*
     * 排序
     */
    private void reorderSections() {
        mKeyedSections.clear();
        int startPosition = 0;
        for (SectionItem<T> item : mSections) {
            mKeyedSections.put(startPosition, item);
            startPosition += item.getCount();
        } 
    }

    @Override
    public int getCount() {
        int count = 0;
        for (SectionItem<T> item : mSections) {
            count += item.getCount();
        }
        return count;
    }

    @Override
    public int getViewTypeCount() {
        //两种View类型:表头,表身
        return 2; 
    }

    @Override
    public int getItemViewType(int position) {
        if (isHeaderAtPosition(position)) {
            return TYPE_HEADER;
        } else {
            return TYPE_ITEM;
        }     
    }

    @Override
    public T getItem(int position) {
        return findSectionItemAtPosition(position);
    }
    
    @Override
    public long getItemId(int position) {
        return position;
    }

    /*
     * false:ListView的item不能点击
     */
    @Override
    public boolean areAllItemsEnabled() {
        return false;
    }

    /*
     * 判断哪个是ListView的表头
     */
    @Override
    public boolean isEnabled(int position) {
        return !isHeaderAtPosition(position);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        switch (getItemViewType(position)) {
            case TYPE_HEADER:
                return getHeaderView(position, convertView, parent);
            case TYPE_ITEM:
                return getItemView(position, convertView, parent);
           default:
                return convertView;
        }
    }

    private View getHeaderView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = mLayoutInflater.inflate(mHeaderResource, parent, false);
        }
        SectionItem<T> item = mKeyedSections.get(position);
        TextView textView = (TextView) convertView.findViewById(android.R.id.text1);
        textView.setText(item.getTitle());
        return convertView;
    }

    private View getItemView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = mLayoutInflater.inflate(mItemResource, parent, false);
        }
        T item = findSectionItemAtPosition(position);
        TextView textView = (TextView) convertView.findViewById(android.R.id.text1);
        textView.setText(item.toString());
        return convertView;
    }

    /** 监听点击事件 */
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        T item = findSectionItemAtPosition(position);
        if (item != null) {
            onSectionItemClick(item);
        }
    }

    /**
     * 监听某段点击事件,由用户决定
     * @item 用户点击的某个item
     */
    public abstract void onSectionItemClick(T item);
    
    /* 帮助映射不同的item到不同的字段内 */
    /*
     * 判断是否是表头
     */
    private boolean isHeaderAtPosition(int position) {
        for (int i=0; i < mKeyedSections.size(); i++) {
            //判断位置是否是表头
            if (position == mKeyedSections.keyAt(i)) {
                return true;
            }
        }
        return false;
    }

    /*
     * 找出全局位置的Item
     */
    private T findSectionItemAtPosition(int position) {
        int firstIndex, lastIndex;
        for (int i=0; i < mKeyedSections.size(); i++) {
            firstIndex = mKeyedSections.keyAt(i);
            lastIndex = firstIndex + mKeyedSections.valueAt(i).getCount();
            if (position >= firstIndex && position < lastIndex) {
                int sectionPosition = position - firstIndex - 1;
                return mKeyedSections.valueAt(i).getItem(sectionPosition);
            }
        }
        return null;
    }
}

10. 创建简洁易懂的自定义组件

举例TextImageButton

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class TextImageButton extends FrameLayout {
    private ImageView imageView;
    private TextView textView;

    public TextImageButton(Context context) {
        this(context, null);
    }
        
    public TextImageButton(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    
    public TextImageButton(Context context, AttributeSet attrs,
            int defaultStyle) {
        // 用默认button风格初始化layout
        // 当前主题设置了可点击属性以及背景
        super(context, attrs, android.R.attr.buttonStyle);
        //创建子类视图
        imageView = new ImageView(context, attrs, defaultStyle);
        textView = new TextView(context, attrs, defaultStyle);
        //为子类视图创建LayoutParams
        FrameLayout.LayoutParams params = new 
            FrameLayout.LayoutParams(
            LayoutParams.WRAP_CONTENT,
            LayoutParams.WRAP_CONTENT, 
            Gravity.CENTER);

        //添加子类视图
        this.addView(imageView, params);
        this.addView(textView, params);

        //如果有图片,显示图片
        if(imageView.getDrawable() != null) {
            textView.setVisibility(View.GONE);
            imageView.setVisibility(View.VISIBLE);
        } else {
            textView.setVisibility(View.VISIBLE);
            imageView.setVisibility(View.GONE);
        }
    }
    
    /* 属性设置 */
    public void setText(CharSequence text) {
        //切换至Text视图
        textView.setVisibility(View.VISIBLE);
        imageView.setVisibility(View.GONE);
        //设置Text值
        textView.setText(text);
    }
    
    public void setImageResource(int resId) {
        //切换至图片视图
        textView.setVisibility(View.GONE);
        imageView.setVisibility(View.VISIBLE);
        //设置图片资源
        imageView.setImageResource(resId);
    }

    public void setImageDrawable(Drawable drawable) {
        //切换至图片视图
        textView.setVisibility(View.GONE);
        imageView.setVisibility(View.VISIBLE);
        //设置图片Drawable
        imageView.setImageDrawable(drawable);
    }
}

11. 自定义过渡动画

  • Activity的用法

在startActivity()或者finish()后立即调用overridePendingTransition()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# startActivity()后的动画
res/anim/activity_open_enter.xml
res/anim/activity_open_exit.xml

# finish()后的动画
res/anim/activity_close_enter.xml
res/anim/activity_close_exit.xml


# res/anim/xxxx.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        android:fromDegrees="90" android:toDegrees="0"
        android:pivotX="0%" android:pivotY="0%"
        android:fillEnabled="true"
        android:fillBefore="true" android:fillAfter="true"
        android:duration="500"  />
    <alpha
        android:fromAlpha="0.0" android:toAlpha="1.0"
        android:fillEnabled="true"
        android:fillBefore="true" android:fillAfter="true"
        android:duration="500" />
</set>

//开启新的acitvity的动画效果
Intent intent = new Intent(...);
startActivity(intent);
overridePendingTransition(R.anim.activity_open_enter, R.anim.activity_open_exit);
//关闭当前activity的动画效果
finish();
overridePendingTransition(R.anim.activity_close_enter, R.anim.activity_close_exit);

# res/values/styles.xml
<resources>
    <style name="AppTheme" parent="android:Theme.Holo.Light">
        <item name="android:windowAnimationStyle">
            @style/ActivityAnimation</item>
    </style>
    
    <style name="ActivityAnimation"
        parent="@android:style/Animation.Activity">
        <item name="android:activityOpenEnterAnimation">
            @anim/activity_open_enter</item>
        <item name="android:activityOpenExitAnimation">
            @anim/activity_open_exit</item>
        <item name="android:activityCloseEnterAnimation">
            @anim/activity_close_enter</item>
        <item name="android:activityCloseExitAnimation">
            @anim/activity_close_exit</item>
    </style>
</resources>
  • 支持库中的Fragments的用法:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    //必须第一个调用
    ft.setCustomAnimations(R.anim.activity_open_enter,
            R.anim.activity_open_exit,
            R.anim.activity_close_enter,
            R.anim.activity_close_exit);
    ft.replace(R.id.container_fragment, fragment);
    ft.addToBackStack(null);
ft.commit();

或者
// 覆写onCreateAnimation()方法
@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    switch (transit) {
        case FragmentTransaction.TRANSIT_FRAGMENT_FADE:
            if (enter) {
                return AnimationUtils.loadAnimation(getActivity(),
                        android.R.anim.fade_in);
            } else {
                return AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_out);
            }
        case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
            if (enter) {
                return AnimationUtils.loadAnimation(getActivity(), R.anim.activity_close_enter);
            } else {
                return AnimationUtils.loadAnimation(getActivity(), R.anim.activity_close_exit);
            }
        case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
        default:
            if (enter) {
                return AnimationUtils.loadAnimation(getActivity(), R.anim.activity_open_enter);
            } else {
                return AnimationUtils.loadAnimation(getActivity(), R.anim.activity_open_exit);
            }
    }
}

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    //设置动画效果
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    ft.replace(R.id.container_fragment, fragment);
    ft.addToBackStack(null);
ft.commit();

12. 创建View的变化

覆写自定义View的getChildStaticTransformation()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Override
protected boolean getChildStaticTransformation(View child, Transformation t) {
    // 清除已有的变化
    t.clear();
    if (getOrientation() == HORIZONTAL) {
        // 基于离左边缘距离来改变子类View
        float delta = 1.0f - ((float) child.getLeft() / getWidth());
        t.getMatrix().setScale(delta, delta, child.getWidth() / 2, child.getHeight() / 2);
    } else {
        //基于离上边缘距离来改变子类View
        float delta = 1.0f - ((float) child.getTop() / getHeight());
        t.getMatrix().setScale(delta, delta, child.getWidth() / 2, child.getHeight() / 2);
        //基于子类View的位置渐变
        t.setAlpha(delta);
    }
    return true;
}

13. 创建可扩展的Collection Views<待续>

14. Action Bar

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# AndroidManifest.xml
<activity
    ...
    android:theme="@style/AppTheme">
</activity>

# res/values/styles.xml
<resources>
    <style name="AppTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar">
        ...
    </style>
</resources>

res/layout/activity_toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- Toolbar widget -->
    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:minHeight="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
    <!-- Remaining application view contents here -->
</LinearLayout>

# Toolbar Activity
public class SupportToolbarActivity extends ActionBarActivity {
    private Toolbar mToolbar;

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

        //定位toolbar
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(mToolbar);
    }
    
    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        /*
         * 在调用onCreate()之后设置toolbar名字
         */
        mToolbar.setTitle("Android Recipes");

        mToolbar.setSubtitle("Toolbar Recipes");
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.support, menu);
        return true;
    }
}

15. 锁定Acitivity方位

1
2
3
4
5
6
7
8
9
# AndroidManifest.xml
<activity
    ...
    android:screenOrientation="portrait">
    ...
</activity>

<activity android:name=".XxxActivity"
    android:screenOrientation="landscape"/>

16. 动态方位锁定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    int current = getResources().getConfiguration().orientation;
    if(isChecked) {
        switch(current) {
            case Configuration.ORIENTATION_LANDSCAPE:
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                break;
            case Configuration.ORIENTATION_PORTRAIT:
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                break;
            default:
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
        }
    } else {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
    } 
}

17. 手动处理屏幕旋转

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class ManualRotationActivity extends Activity {

    private EditText mEditText;
    private CheckBox mCheckBox;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //载入视图
        loadView();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        //必需调用super
        super.onConfigurationChanged(newConfig);
        //只重载配置下的视图
        if (mCheckBox.isChecked()) {
            final Bundle uiState = new Bundle();
            //保存UI中重要的状态
            saveState(uiState);
            //重载视图
            loadView();
            //重载UI状态
            restoreState(uiState);
        }
    }
    
    //持久化UI状态
    private void saveState(Bundle state) {
        state.putBoolean("checkbox", mCheckBox.isChecked());
        state.putString("text", mEditText.getText().toString());
    }

    //重载UI状态
    private void restoreState(Bundle state) {
        mCheckBox.setChecked(state.getBoolean("checkbox"));
        mEditText.setText(state.getString("text"));
    }
    
    //初始化视图
    private void loadView() {
        setContentView(R.layout.activity_manual);

        mCheckBox = (CheckBox) findViewById(R.id.override);
        mEditText = (EditText) findViewById(R.id.text);
    }
}

17. 创建全局的Actions

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class ContextListItem extends LinearLayout implements PopupMenu.OnMenuItemClickListener, View.OnClickListener {
    private PopupMenu mPopupMenu;
    private TextView mTextView;
    public ContextListItem(Context context) {
        super(context);
    }
    public ContextListItem(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public ContextListItem(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mTextView = (TextView) findViewById(R.id.text);
        //添加点击事件
        View contextButton = findViewById(R.id.context);
        contextButton.setOnClickListener(this);
        //创建全局菜单
        mPopupMenu = new PopupMenu(getContext(), contextButton);
        mPopupMenu.setOnMenuItemClickListener(this);
        mPopupMenu.inflate(R.menu.contextmenu);
    }
    
    @Override
    public void onClick(View v) {
        //处理全局按钮点击,显示菜单
        mPopupMenu.show();
    }
    
    @Override
    public boolean onMenuItemClick(MenuItem item) {
        String itemText = mTextView.getText().toString();
        switch (item.getItemId()) {
            case R.id.menu_edit:
                Toast.makeText(getContext(), "Edit "+itemText, Toast.LENGTH_SHORT).show();
                break;
            case R.id.menu_delete:
                Toast.makeText(getContext(), "Delete "+itemText, Toast.LENGTH_SHORT).show();
                break; 
        }
        return true;
    }
}

18. 显示会话框

19. 自定义菜单和Actions

20. 自定义BACK

1
2
3
4
5
6
@Override
public void onBackPressed() {
    //自定义BACK按钮
    //调用super是正常处理 (比如说清除Acitivity)
    super.onBackPressed();
}

21. 效仿HOME按钮

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);

# 示例
@Override
public void onBackPressed() {
    Intent intent = new Intent(Intent.ACTION_MAIN);
    intent.addCategory(Intent.CATEGORY_HOME);
    startActivity(intent);
}

22. TextView监控

实现接口:android.text.TextWatcher

1
2
3
public void beforeTextChanged(CharSequence s, int start, int count, int after);
public void onTextChanged(CharSequence s, int start, int before, int count);
public void afterTextChanged(Editable s);

23. 自定义键盘动作

实现接口:TextView.OnEditorActionListener

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
    if(actionId == EditorInfo.IME_ACTION_SEARCH) {
        //处理Search键
        return true;
    }
    if(actionId == EditorInfo.IME_ACTION_GO) {
        //处理go键
        return true;
    }
    return false;
}

24. 禁用Soft键盘

1
2
3
4
public void onClick(View view) {
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}

25. 处理复杂的TOUCH事件

26. 转发TOUCH事件

覆写Layout类中的onSizeChanged()方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        if (w != oldw || h != oldh) {
            //Apply the whole area of this view as the delegate area
            Rect bounds = new Rect(0, 0, w, h);
            TouchDelegate delegate = new TouchDelegate(bounds, mButton);
            setTouchDelegate(delegate);
        }
    }

27. 转移TOUCH到子类View