- 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
onMeasureSquareViewonDrawPizzaContainer for positioning child views
onLayoutPhotoSpiraldispatchDrawSidewaysLayout