Layouts与Views
16lz
2021-01-24
<Android诀窍> Layouts与Views
字数2466 阅读1565 评论4 喜欢171. 统一重复组件风格
# 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<待续>
<完>
阅读原文
更多相关文章
- android悬浮窗的两种实现方案
- android 日历组件(CalendarView)
- 深入理解LayoutInflater
- Android(安卓)3.0 r1中文API文档(103) —— InputMethodManager
- ConstraintLayout (约束布局)属性详情
- Android学习之路四:ImageView
- Android手动显示和隐藏软键盘
- 阅读《Android(安卓)从入门到精通》(23)——列表视图
- Android(安卓)手动显示和隐藏软键盘