Android UI Components, Activity Lifecycle, and Data Persistence
LinearLayout and RelativeLayout
Android provides two primary layout managers for structuring user interfaces: LinearLayout and RelativeLayout. Their most common configuration attributes are listed below.
LinearLayout
LinearLayout aligns children in a single direction—vertical or horizontal—using the android:orientation flag. The table summarizes several frequently used attributes.
| Attribute | Description |
|---|---|
android:id |
Unique identifire |
android:layout_width |
Width |
android:layout_height |
Height |
android:layout_margin |
Outer spacing |
android:layout_padding |
Inner spacing |
android:orientation |
vertical or horizontal |
android:background |
Background |
The following layout demonstrates spacing and weight distribution:
<?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="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/top_panel"
android:layout_width="200dp"
android:layout_height="200dp"
android:orientation="vertical"
android:background="#000000"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingTop="50dp"
android:paddingBottom="10dp"
android:layout_marginBottom="20dp">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF0033"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#0066FF"
android:orientation="horizontal"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp">
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#000000"
android:layout_weight="1"/>
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#FF0033"
android:layout_weight="2"/>
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#55AA99"
android:layout_weight="2"/>
</LinearLayout>
</LinearLayout>
RelativeLayout
RelativeLayout positions children relative to siblings or the parent boundaries. The following table highlights positioning attributes beyond those shared with LinearLayout.
| Attribute | Description |
|---|---|
android:layout_toLeftOf |
Left of target view |
android:layout_toRightOf |
Right of target view |
android:layout_alignBottom |
Align bottom with target |
android:layout_alignParentBottom |
Align with parent bottom |
android:layout_below |
Below target view |
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/block_a"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#000000" />
<View
android:id="@+id/block_b"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_below="@id/block_a"
android:background="#FF0033" />
<LinearLayout
android:id="@+id/row_container"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_below="@id/block_b"
android:orientation="horizontal"
android:background="#0066FF"
android:padding="15dp">
<View
android:layout_width="100dp"
android:layout_height="match_parent"
android:background="#FF0033"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:padding="15dp">
<View
android:id="@+id/left_block"
android:layout_width="100dp"
android:layout_height="match_parent"
android:background="#FF9900" />
<View
android:id="@+id/right_block"
android:layout_width="100dp"
android:layout_height="match_parent"
android:layout_toRightOf="@id/left_block"
android:layout_marginLeft="10dp"
android:background="#FF9900" />
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
Interactive UI Widgets
TextView
Text can be styled with size, color, ellipsize behavior, icons, and decorations such as strikethrough or underline. A marquee effect requires singleLine and ellipsize="marquee" with focusability enabled. The Java snippet below applies programmatic formatting:
public class TextStylingActivity extends AppCompatActivity {
private TextView strikeView;
private TextView underlineView;
private TextView marqueeView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_text_styling);
strikeView = findViewById(R.id.tv_strike);
strikeView.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
strikeView.getPaint().setAntiAlias(true);
underlineView = findViewById(R.id.tv_underline);
underlineView.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG);
marqueeView = findViewById(R.id.tv_marquee);
marqueeView.setSelected(true);
}
}
Button
Custom backgrounds defined in drawable resources can manage corner radius and press feedback. Click handling occurs via programmatic OnClickListener or the onClick XML attribute.
<Button
android:id="@+id/btn_rounded"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="Action"
android:textSize="20sp"
android:textColor="#FFFFFF"
android:background="@drawable/bg_rounded_button"
android:onClick="onButtonPressed"/>
Listener assignment inside Activity:
Button actionBtn = findViewById(R.id.btn_rounded);
actionBtn.setOnClickListener(v -> {
Toast.makeText(this, "Pressed", Toast.LENGTH_SHORT).show();
startActivity(new Intent(this, TargetScreen.class));
});
EditText, RadioButton, CheckBox
Each widget offers standard attributes plus custom drawable for checked states. Event listeners such as OnCheckedChangeListener handle selection:
CheckBox checkBox = findViewById(R.id.cb_custom);
checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
String message = isChecked ? "Checked" : "Unchecked";
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
});
ImageView
Scale type constants control image fitting: fitCenter preserves ratio, centerCrop fills bounds and crops excess, and fitXY stretches to fill regardless of ratio. Third‑party libraries like Glide simplify network image loading:
Glide.with(context).load("https://example.com/image.png").into(imageView);
ListView and RecyclerView
ListView
ListView relies on an adapter, often a custom BaseAdapter, to supply rows. View recycling improves performance.
Adapter implementation with view holder pattern:
public class ArticleAdapter extends BaseAdapter {
private final Context context;
private final LayoutInflater inflater;
public ArticleAdapter(Context context) {
this.context = context;
inflater = LayoutInflater.from(context);
}
@Override
public int getCount() { return 10; }
@Override
public Object getItem(int position) { return null; }
@Override
public long getItemId(int position) { return 0; }
static class RowHolder {
ImageView thumbnail;
TextView title;
TextView date;
TextView summary;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
RowHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.row_article, parent, false);
holder = new RowHolder();
holder.thumbnail = convertView.findViewById(R.id.iv_thumb);
holder.title = convertView.findViewById(R.id.tv_title);
holder.date = convertView.findViewById(R.id.tv_date);
holder.summary = convertView.findViewById(R.id.tv_summary);
convertView.setTag(holder);
} else {
holder = (RowHolder) convertView.getTag();
}
holder.title.setText("Sample Title");
holder.date.setText("2088-08-08");
holder.summary.setText("Brief description...");
Glide.with(context).load("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png").into(holder.thumbnail);
return convertView;
}
}
Item click handling:
listView.setOnItemClickListener((parent, view, position, id) -> {
Toast.makeText(this, "Position: " + position, Toast.LENGTH_SHORT).show();
});
listView.setOnItemLongClickListener((parent, view, position, id) -> {
Toast.makeText(this, "Long press: " + position, Toast.LENGTH_SHORT).show();
return true;
});
RecyclerView
RecyclerView offers improved flexibility for lists, grids, and staggered layouts. Different ViewHolder subtypes can coexist within the same adapter. The XRecyclerView library extends it with header, footer, pull‑to‑refresh, and infinite scroll.
ScrollView and HorizontalScrollView
ScrollView permits vertical scrolling of a single child, while HorizontalScrollView enables horizontal scrolling. The following layout stacks buttons vertically and adds a horizontally scrollable row:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/btn_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"/>
<Button
android:id="@+id/btn_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button"/>
<HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:layout_width="200dp"
android:layout_height="300dp"
android:text="Item A"/>
<Button
android:layout_width="200dp"
android:layout_height="300dp"
android:text="Item B"/>
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
</ScrollView>
WebView
WebView loads remote URLs, local assets, or raw HTML. Navigation history is controlled with goBack() and goForward().
WebView browser = findViewById(R.id.web_view);
browser.getSettings().setJavaScriptEnabled(true);
browser.loadUrl("https://example.com");
Override back press for in‑page navigation:
@Override
public void onBackPressed() {
if (browser.canGoBack()) {
browser.goBack();
} else {
super.onBackPressed();
}
}
Overlay and Dialog Components
Toast
A custom Toast can include a layout graph or reposition itself:
Toast toast = Toast.makeText(context, "Saved", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 100);
toast.show();
AlertDialog
AlertDialog supports single‑choice, multi‑choice, and custom view layouts.
ProgressBar and ProgressDialog
Indeterminate progress indicators can use custom drawables:
<ProgressBar
android:id="@+id/custom_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/MyProgressBar"/>
Custom Dialog
Create a style extending Theme.AppCompat.Light.Dialog and inflate a custom layout.
PopupWindow
PopupWindow displays anchored floating content; its appearance and position are controlled programmatically.
Activity Lifecycle and Navigation
Lifecycle Callbacks
The essential lifecycle sequence includes onCreate, onStart, onResume, onPause, onStop, onRestart, and onDestroy. Transitions like backgrounding invoke onPause and onStop; returning triggers onRestart, onStart, and onResume.
Explicit and Implicit Intents
Explicit navigation targets a concrete Activity class; implicit intents match action and category filters.
Intent explicit = new Intent(this, ProfileActivity.class);
explicit.putExtra("user_id", 42);
startActivity(explicit);
Result‑oriented launches utilise ActivityResultLauncher with the Activity Result API.
Launch Modes
Configure the android:launchMode attribute to one of the following:
standard– new instance every time.singleTop– reuse existing top instance;onNewIntentdelivered.singleTask– clear activities above the target in the same task.singleInstance– isolated single instance in its own task.
Fragments
Fragments possess their own lifecycle and rely on a host Activity. Communication occurs via interfaces or direct method calls through getActivity().
When replacing Fragments, using hide() instead of replace() presevres view state upon back navigation.
Fragment current = getParentFragmentManager().findFragmentByTag("root_fragment");
if (current != null) {
getParentFragmentManager().beginTransaction()
.hide(current)
.add(R.id.container, new DetailFragment())
.addToBackStack(null)
.commit();
}
Event Handling
Listener‑Based
Common listener interfaces include OnClickListener and OnTouchListener. When multiple listeners of the same type are set, the latest overwrites previous ones.
Callback‑Based
Touch events propagate from the view’s own onTouchEvent to the Activity’s method unless consumed earlier.
View Event Dispatching
dispatchTouchEvent checks OnTouchListener first; if it returns false, onTouchEvent processes the event and may trigger onClick or onLongClick.
Handler
Handler schedules future work and enables cross‑thread communication.
Handler uiHandler = new Handler(Looper.getMainLooper());
uiHandler.postDelayed(() -> {
navigateToHome();
}, 3000);
new Thread(() -> {
Message msg = Message.obtain();
msg.what = 101;
uiHandler.sendMessage(msg);
}).start();
Property Animation
Property animations modify actual object attributes. ObjectAnimator targets translation, alpha, rotation, or scale.
ObjectAnimator mover = ObjectAnimator.ofFloat(targetView, "translationY", 0f, 500f, 200f, 800f);
mover.setDuration(2000);
mover.start();
Local Data Storage
SharedPreferences
Lightweight key‑value pairs stored in XML.
SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("user_token", token);
editor.apply();
String storedToken = prefs.getString("user_token", "");
Files reside under /data/data/<package>/shared_prefs/.
Internal File Storage
Read and write through FileInputStream/FileOutputStream using openFileInput and openFileOutput:
private void persistPayload(String payload, String filename) {
try (FileOutputStream fos = openFileOutput(filename, MODE_PRIVATE)) {
fos.write(payload.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
private String readPayload(String filename) {
StringBuilder builder = new StringBuilder();
try (FileInputStream fis = openFileInput(filename)) {
byte[] buffer = new byte[1024];
int length;
while ((length = fis.read(buffer)) > 0) {
builder.append(new String(buffer, 0, length));
}
} catch (IOException e) {
e.printStackTrace();
}
return builder.toString();
}
External File Storage
On modern Android versions, app‑specific external directories are accessed via getExternalFilesDir(null):
String basePath = getExternalFilesDir(null).getAbsolutePath();
File dir = new File(basePath, "uploads");
if (!dir.exists()) dir.mkdirs();
File file = new File(dir, "log.txt");
// stream operations accordingly
Local Broadcasts
LocalBroadcastManager confines broadcasts within the app. Register a dynamic receiver and unregister in onDestroy.
BroadcastReceiver refreshReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if ("com.internal.REFRESH".equals(intent.getAction())) {
updateContent();
}
}
};
LocalBroadcastManager.getInstance(this)
.registerReceiver(refreshReceiver, new IntentFilter("com.internal.REFRESH"));
// Sending side
Intent intent = new Intent("com.internal.REFRESH");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
Ripple Effect on Custom Shapes
Rounded views can incorporate material ripple with a <ripple> drawable:
<!-- ripple_rounded.xml -->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#22000000">
<item android:drawable="@drawable/shape_rounded_green" />
</ripple>
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/ripple_rounded"
android:elevation="8dp"
android:gravity="center"
android:text="Sign In"
android:textColor="@android:color/white"
android:textSize="18sp" />