- TextView
- EditText
- Button
- Spinner
- DatePicker
Simple View | Container | Compound Control |
---|---|---|
↳ TextView ↳ EditText ↳ Button ↳ ImageView ↳ ImageButton |
↳ AdapterView ↳ ListView ↳ Gallery ↳ GridView ↳ LinearLayout ↳ RelativeLayout ↳ FrameLayout ↳ TableLayout |
↳ DatePicker ↳ TwoLineListItem |
<TextView android:id="@+id/date" android:layout_width="match_parent" android:layout_height="wrap_content" />
TextView dateView = (TextView) findViewById(R.id.date); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); String today = dateFormat.format(Calendar.getInstance().getTime()); dateView.setText(today);
package com.sqisland.android.dateview;
public class DateView extends TextView {
public DateView(Context context) {
super(context);
}
public DateView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DateView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
}
public class DateView extends TextView { public DateView(Context context) { super(context); setDate(); } public DateView(Context context, AttributeSet attrs) { super(context, attrs); setDate(); } public DateView( Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setDate(); } }
private void setDate() { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); String today = dateFormat.format(Calendar.getInstance().getTime()); setText(today); // self = DateView = subclass of TextView }
public class DateActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); DateView dateView = new DateView(this); setContentView(dateView); } }
public class DateActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } }
<com.sqisland.android.dateview.DateView android:layout_width="match_parent" android:layout_height="wrap_content" />
<com.sqisland.android.dateview.DateView android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#0f0" android:textSize="40sp" />
<merge xmlns:android="http://schemas.android.com/apk/res/android" > <Button android:id="@+id/minus_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/minus" /> <TextView android:id="@+id/text" android:layout_width="50dp" android:layout_height="wrap_content" android:gravity="center" /> <Button android:id="@+id/plus_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/plus" /> </merge>
// Called from constructor private void init() { LayoutInflater inflater = (LayoutInflater) getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.length_picker, this); mPlusButton = findViewById(R.id.plus_button); mTextView = (TextView) findViewById(R.id.text); mMinusButton = findViewById(R.id.minus_button); updateControls(); mPlusButton.setOnClickListener(this); mMinusButton.setOnClickListener(this); }
private void updateControls() { int feet = mNumInches / 12; int inches = mNumInches % 12; String text = String.format("%d' %d\"", feet, inches); if (feet == 0) { text = String.format("%d\"", inches); } else { if (inches == 0) { text = String.format("%d'", feet); } } mTextView.setText(text); mMinusButton.setEnabled(mNumInches > 0); }
public void onClick(View v) { switch (v.getId()) { case R.id.plus_button: mNumInches++; updateControls(); break; case R.id.minus_button: if (mNumInches > 0) { mNumInches--; updateControls(); } break; } }
public int getNumInches() { return mNumInches; }
public Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable("superState", super.onSaveInstanceState()); bundle.putInt("numInches", mNumInches); return bundle; } public void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; mNumInches = bundle.getInt("numInches"); super.onRestoreInstanceState(bundle.getParcelable("superState")); } else { super.onRestoreInstanceState(state); } updateControls(); }
<com.sqisland.android.length_picker.LengthPicker android:id="@+id/width" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <com.sqisland.android.length_picker.LengthPicker android:id="@+id/height" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/area" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/compute" android:onClick="updateArea" />
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mWidth = (LengthPicker) findViewById(R.id.width); mHeight = (LengthPicker) findViewById(R.id.height); mArea = (TextView) findViewById(R.id.area); } public void onResume() { super.onResume(); updateArea(null); } public void updateArea(View v) { int area = mWidth.getNumInches() * mHeight.getNumInches(); mArea.setText(getString(R.string.area_format, area)); }
requestLayout
up to ViewRootmeasure
all childrenlayout
all childrenonMeasure
- how big?onDraw
- what to show?void onMeasure(int widthMeasureSpec, int heightMeasureSpec);
setMeasuredDimension
to store resultsUNSPECIFIED
- anything goes!
AT_MOST
- as large as the specified size
EXACTLY
- as given by parent
public void onMeasure(int widthSpec, int heightSpec) { super.onMeasure(widthSpec, heightSpec); int size = Math.min(getMeasuredWidth(), getMeasuredHeight()); setMeasuredDimension(size, size); }
void onDraw(Canvas canvas);
private void init() { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.YELLOW); mPaint.setStrokeWidth(STROKE_WIDTH); mPaint.setStyle(Paint.Style.STROKE); }
public void onDraw(Canvas canvas) { int width = getWidth() - getPaddingLeft() - getPaddingRight(); int height = getHeight() - getPaddingTop() - getPaddingBottom(); int diameter = Math.min(width, height) - STROKE_WIDTH; int cx = getPaddingLeft() + width / 2; int cy = getPaddingTop() + height / 2; int radius = diameter / 2; canvas.drawCircle(cx, cy, radius, mPaint); drawPizzaCuts(canvas, cx, cy, radius); }
private void drawPizzaCuts( Canvas canvas, float cx, float cy, float radius) { canvas.save(); final float degrees = 360f / NUM_WEDGES; for (int i = 0; i < NUM_WEDGES; ++i) { canvas.rotate(degrees, cx, cy); canvas.drawLine(cx, cy, cx, cy - radius, mPaint); } canvas.restore(); }
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <com.sqisland.android.pizza.Pizza android:layout_width="200dp" android:layout_height="200dp" android:padding="20dp" android:background="#555" /> <com.sqisland.android.pizza.Pizza android:layout_width="200dp" android:layout_height="200dp" android:layout_margin="20dp" android:background="#555" /> </LinearLayout>
onMeasure
- how big are the children?onLayout
- where are the children?dispatchDraw
- what to show above or below the children?protected void onMeasure(int widthSpec, int heightSpec) { measureChildren(widthSpec, heightSpec); View first = getChildAt(0); int size = first.getMeasuredWidth() + first.getMeasuredHeight(); int width = ViewGroup.resolveSize(size, widthSpec); int height = ViewGroup.resolveSize(size, heightSpec); setMeasuredDimension(width, height); }
protected void onLayout( boolean changed, int l, int t, int r, int b) { View first = getChildAt(0); final int childWidth = first.getMeasuredWidth(); final int childHeight = first.getMeasuredHeight(); int x = getWidth() / 2 - (childWidth - childHeight) / 2; int y = getHeight() / 2 - (childWidth + childHeight) / 2; for (int i = 0; i < getChildCount(); ++i) { View child = getChildAt(i); x = adjustX(i, x, childWidth, childHeight); y = adjustY(i, y, childWidth, childHeight); child.layout(x, y, x + child.getMeasuredWidth(), y + child.getMeasuredHeight()); } }
private int adjustX(int pos, int x, int childWidth, int childHeight) { int delta = childWidth - childHeight; switch (pos) { case 1: return x + delta; case 2: return x - childWidth; } return x; }
private int adjustY(int pos, int y, int childWidth, int childHeight) { int delta = childWidth - childHeight; switch (pos) { case 1: return y + childHeight; case 2: return y + delta; case 3: return y - childWidth; } return y; }
<com.sqisland.android.sideways_layout.SidewaysLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#f0f12b" android:text="@string/good_night_moon" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textStyle="bold" android:text="@string/green_eggs_and_ham" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/matilda" /> </com.sqisland.android.sideways_layout.SidewaysLayout>
public class SidewaysLayout extends LinearLayout {
// Constructors omitted
protected void onMeasure(int widthSpec, int heightSpec) {
super.onMeasure(widthSpec, heightSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void dispatchDraw(Canvas canvas) {
canvas.save();
canvas.translate(0, getHeight());
canvas.rotate(-90, 0, 0);
super.dispatchDraw(canvas);
canvas.restore();
}
}
protected void onMeasure(int widthSpec, int heightSpec) { super.onMeasure(widthSpec, heightSpec); setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth()); }
protected void dispatchDraw(Canvas canvas) { canvas.save(); canvas.translate(0, getHeight()); canvas.rotate(-90, 0, 0); super.dispatchDraw(canvas); canvas.restore(); }
<com.sqisland.android.pizza.Pizza android:layout_width="120dp" android:layout_height="120dp" pizza:color="#f00" pizza:num_wedges="4" />
<resources> <declare-styleable name="com.sqisland.android.pizza.Pizza"> <attr name="stroke_width" format="integer" /> <attr name="num_wedges" format="integer" /> <attr name="color" format="color" /> </declare-styleable> </resources>
private void init(AttributeSet attrs) { if (attrs != null) { String namespace = "http://schemas.android.com/apk/res-auto"; mStrokeWidth = attrs.getAttributeIntValue( namespace, "stroke_width", DEFAULT_STROKE_WIDTH); mNumWedges = attrs.getAttributeIntValue( namespace, "num_wedges", DEFAULT_NUM_WEDGES); mColor = attrs.getAttributeIntValue( namespace, "color", DEFAULT_COLOR); }
http://schemas.android.com/apk/res-auto
with your app package
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:pizza="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" > <com.sqisland.android.pizza.Pizza android:layout_width="120dp" android:layout_height="120dp" pizza:color="#f00" pizza:num_wedges="4" /> <com.sqisland.android.pizza.Pizza android:layout_width="160dp" android:layout_height="160dp" pizza:stroke_width="15" pizza:num_wedges="6" /> </LinearLayout>
Modularize repeated code
DateView extends TextView
LengthPicker
Customize size and appearance of the View
onMeasure
SquareView
onDraw
Pizza
Container for positioning child views
onLayout
PhotoSpiral
dispatchDraw
SidewaysLayout