<Android诀窍> Layouts与Views

字数2466 阅读1565 评论4

1. 统一重复组件风格

# 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

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

# 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

# Itempublic 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;    }}# Adapterpublic 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

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()

# startActivity()后的动画res/anim/activity_open_enter.xmlres/anim/activity_open_exit.xml# finish()后的动画res/anim/activity_close_enter.xmlres/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的用法:
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()方法@Overridepublic 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()

@Overrideprotected 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<待续>

<完>

阅读原文

更多相关文章

  1. android悬浮窗的两种实现方案
  2. android 日历组件(CalendarView)
  3. 深入理解LayoutInflater
  4. Android(安卓)3.0 r1中文API文档(103) —— InputMethodManager
  5. ConstraintLayout (约束布局)属性详情
  6. Android学习之路四:ImageView
  7. Android手动显示和隐藏软键盘
  8. 阅读《Android(安卓)从入门到精通》(23)——列表视图
  9. Android(安卓)手动显示和隐藏软键盘

随机推荐

  1. 6组精美的纯CSS3悬浮飘带特效
  2. 向钢铁侠学习怎样开发软件[每日前端夜话0
  3. 超酷的 CSS3 复古风格 3D 按钮
  4. HTML5 3D魔方动画
  5. 啥?听说你还在手写复杂的参数校验?
  6. 分析ARM ANDROID市场及技术结合点 转载
  7. 以后要是再写for循环,我就捶自己
  8. HTML5/CSS3 实现漂亮的 3D 滑杆插件
  9. jQuery/CSS3邮票边框的照片墙相册
  10. 2020开年,C语言重回巅峰王座!