A. Android入门教程 | Fragment 基础概念
Fragment,直译为“碎片”,“片段”。Fragment表示FragmentActivity中的行为或界面的一部分。可以在一个Activity中组合多个片段,从而构建多窗格界面,并在多个Activity中重复使用某个片段。可以将片段视为Activity的模块化组成部分,它具有自己的生命周期,能接收自己的输入事件,并且可以在Activity运行时添加或移除片段(这有点像可以在不同Activity中重复使用的“子Activity”)。
片段必须始终托管在Activity中,其生命周期直接受宿主Activity生命周期的影响。例如,当Activity暂停时,Activity的所有片段也会暂停;当Activity被销毁时,所有片段也会被销毁。
不过,当Activity正在运行(处于已恢复生命周期状态)时,可以独立操纵每个片段,如添加或移除片段。当执行此类片段事务时,也可将其添加到由Activity管理的返回栈 — Activity中的每个返回栈条目都是一条已发生片段事务的记录。借助返回栈,用户可以通过按返回按钮撤消片段事务(后退)。
Fragment的优点包括其代码与Activity非常相似,包含与Activity类似的回调方法,如onCreate()、onStart()、onPause() 和onStop()。实际上,如果要将现有Android应用转换为使用片段,可能只需将代码从Activity的回调方法移入片段相应的回调方法中。通常,至少应实现以下生命周期方法。
片段通常用作Activity界面的一部分,并且会将其自己的布局融入Activity。如要为片段提供布局,必须实现onCreateView()回调方法,Android系统会在片段需要绘制其布局时调用该方法。此方法的实现所返回的View必须是片段布局的根视图。如要从onCreateView()返回布局,可以通过XML中定义的布局资源来扩展布局。为帮助您执行此操作,onCreateView()提供了一个LayoutInflater对象。例如,以下这个Fragment子类从example_fragment.xml文件加载布局:。
接下来,需将该片段添加到您的Activity中。
通常,片段会向宿主Activity贡献一部分界面,作为Activity整体视图层次结构的一部分嵌入到Activity中。可以通过两种方式向Activity布局添加片段。静态方式在Activity的布局文件内声明片段。java代码加载Fragment通过编程方式将片段添加到某个现有ViewGroup。如要在Activity中执行片段事务(如添加、移除或替换片段),则必须使用FragmentTransaction中的API。
在Activity中使用片段的一大优点是,可以通过片段执行添加、移除、替换以及其他操作,从而响应用户交互。提交给Activity的每组更改均称为事务,并且可使用FragmentTransaction中的API来执行一项事务。也可将每个事务保存到由Activity管理的返回栈内,从而让用户能够回退片段更改(类似于回退Activity)。
每个事务都是想要同时执行的一组更改。可以使用add()、remove()和replace()等方法,为给定事务设置您想要执行的所有更改。然后,如要将事务应用到Activity,必须调用commit()。不过,在调用commit()之前,可能希望调用addToBackStack(),以将事务添加到片段事务返回栈。该返回栈由Activity管理,允许用户通过按返回按钮返回上一片段状态。
在 Activity中使用Fragment的一大优点是,可以通过片段执行添加、移除、替换以及其他操作,从而响应用户交互。每个事务都是想要同时执行的一组更改。可以使用add()、remove()和replace()等方法,为给定事务设置您想要执行的所有更改。然后,如要将事务应用到Activity,必须调用commit()。不过,在调用commit()之前,可能希望调用addToBackStack(),以将事务添加到片段事务返回栈。该返回栈由Activity管理,允许用户通过按返回按钮返回上一片段状态。
在 Activity中使用Fragment的一大优点是,可以通过片段执行添加、移除、替换以及其他操作,从而响应用户交互。每个事务都是想要同时执行的一组更改。可以使用add()、remove()和replace()等方法,为给定事务设置您想要执行的所有更改。然后,如要将事务应用到Activity,必须调用commit()。不过,在调用commit()之前,可能希望调用addToBackStack(),以将事务添加到片段事务返回栈。该返回栈由Activity管理,允许用户通过按返回按钮返回上一片段状态。
B. android 自定义switch样式
在修改后的MySwitch控件中,接口基本与原Switch控件一致,并且新增了两项功能。首先,用户可以使用Track背景图片的方式代替文字来表示开关状态的变化,这使得界面更加美观且可定制。其次,可以调整Switch的高度,以适应不同的设计需求。这些改动使得MySwitch控件更加灵活和实用。
以下为关键代码片段:
java
public class MySwitch extends CompoundButton {
// 支持使用Track背景图片表示开关状态
private Drawable mTrackOnDrawable;
private Drawable mTrackOffDrawable;
// 支持设置Switch的最小高度
private int mSwitchMinHeight;
/**
* 构造函数,根据给定的主题属性确定控件的样式,并覆盖特定的样式属性。
* @param context Context,用于确定此控件的主题。
* @param attrs 属性规范,用于指定应偏离默认样式属性。
* @param defStyle 一个属性ID,位于当前主题中,包含此控件的默认样式引用。例如,android.R.attr.switchStyle。
*/
public MySwitch(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
Resources res = getResources();
mTextPaint.density = res.getDisplayMetrics().density;
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Switch, defStyle, 0);
mTrackOnDrawable = a.getDrawable(R.styleable.Switch_trackOn);
mTrackOffDrawable = a.getDrawable(R.styleable.Switch_trackOff);
if (checkTrackOffOnDrawable()) {
mTrackDrawable = mTrackOffDrawable;
} else {
mTrackDrawable = a.getDrawable(R.styleable.Switch_track);
}
mThumbDrawable = a.getDrawable(R.styleable.Switch_thumb);
mTextOn = a.getText(R.styleable.Switch_textOn);
mTextOff = a.getText(R.styleable.Switch_textOff);
mThumbTextPadding = a.getDimensionPixelSize(R.styleable.Switch_thumbTextPadding, 0);
mSwitchMinWidth = a.getDimensionPixelSize(R.styleable.Switch_switchMinWidth, 0);
mSwitchMinHeight = a.getDimensionPixelSize(R.styleable.Switch_switchMinHeight, 0);
mSwitchPadding = a.getDimensionPixelSize(R.styleable.Switch_switchPadding, 0);
int appearance = a.getResourceId(R.styleable.Switch_switchTextAppearance, 0);
if (appearance != 0) {
setSwitchTextAppearance(context, appearance);
}
a.recycle();
ViewConfiguration config = ViewConfiguration.get(context);
mTouchSlop = config.getScaledTouchSlop();
mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
refreshDrawableState();
setChecked(isChecked());
}
private boolean checkTrackOffOnDrawable() {
return mTrackOnDrawable != null && mTrackOffDrawable != null;
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOnLayout == null) {
mOnLayout = makeLayout(mTextOn);
}
if (mOffLayout == null) {
mOffLayout = makeLayout(mTextOff);
}
mTrackDrawable.getPadding(mTempRect);
final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth());
final int switchWidth = Math.max(mSwitchMinWidth, maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right);
int switchHeight; if (mSwitchMinHeight <= 0) {
switchHeight = mTrackDrawable.getIntrinsicHeight();
} else {
switchHeight = Math.max(mSwitchMinHeight, mTempRect.top + mTempRect.bottom);
}
mThumbWidth = maxTextWidth + mThumbTextPadding * 2;
mSwitchWidth = switchWidth;
mSwitchHeight = switchHeight;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int measuredHeight = getMeasuredHeight();
if (measuredHeight < switchHeight) {
setMeasuredDimension(getMeasuredWidthAndState(), switchHeight);
}
}
@Override
public void setChecked(boolean checked) {
if (checkTrackOffOnDrawable()) {
mTrackDrawable = checked ? mTrackOnDrawable : mTrackOffDrawable;
refreshDrawableState();
}
super.setChecked(checked);
mThumbPosition = checked ? getThumbScrollRange() : 0;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 绘制开关
int switchLeft = mSwitchLeft;
int switchTop = mSwitchTop;
int switchRight = mSwitchRight;
int switchBottom = mSwitchBottom;
if (checkTrackOffOnDrawable()) {
mTrackDrawable = getTargetCheckedState() ? mTrackOnDrawable : mTrackOffDrawable;
refreshDrawableState();
}
mTrackDrawable.setBounds(switchLeft, switchTop, switchRight, switchBottom);
mTrackDrawable.draw(canvas);
canvas.save();
mTrackDrawable.getPadding(mTempRect);
int switchInnerLeft = switchLeft + mTempRect.left;
int switchInnerTop = switchTop + mTempRect.top;
int switchInnerRight = switchRight - mTempRect.right;
int switchInnerBottom = switchBottom - mTempRect.bottom;
canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);
mThumbDrawable.getPadding(mTempRect);
final int thumbPos = (int) (mThumbPosition + 0.5f);
int thumbLeft = switchInnerLeft - mTempRect.left + thumbPos;
int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + mTempRect.right;
mThumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
mThumbDrawable.draw(canvas);
// mTextColors 不应为null,但以防万一
if (mTextColors != null) {
mTextPaint.setColor(mTextColors.getColorForState(getDrawableState(), mTextColors.getDefaultColor()));
}
mTextPaint.drawableState = getDrawableState();
Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
if (switchText != null) {
canvas.translate((thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2, (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2);
switchText.draw(canvas);
}
canvas.restore();
}
}
关键属性声明如下:
java
public static final int switchMinHeight;
public static final int switchMinWidth;
public static final int switchPadding;
public static final int switchTextAppearance;
public static final int thumbTextPadding;