Section II Q-2 Alt: Canvas Shapes / SQLite Database Operations (5 Marks)
Question
a) Explain the different shapes available in Android's Canvas and how to draw them.
OR
b) Explain the process of closing and deleting an SQLite database in Android
Answer A: Canvas Shapes in Android
Overview of Canvas Shapes
Android Canvas provides various methods to draw different geometric shapes. Each shape has specific methods and can be customized using Paint objects.
Available Shapes and Drawing Methods
1. Rectangle Shapes
Basic Rectangle:
// Draw filled rectangle
canvas.drawRect(left, top, right, bottom, paint);
// Draw rectangle using RectF
RectF rectF = new RectF(50, 50, 200, 150);
canvas.drawRect(rectF, paint);
Rounded Rectangle:
// Draw rounded rectangle
RectF roundRect = new RectF(50, 200, 250, 300);
canvas.drawRoundRect(roundRect, 20, 20, paint); // rx, ry for corner radius
2. Circle and Oval Shapes
Circle:
// Draw circle
canvas.drawCircle(centerX, centerY, radius, paint);
// Example
canvas.drawCircle(150, 150, 75, paint);
Oval (Ellipse):
// Draw oval using RectF bounds
RectF ovalBounds = new RectF(100, 250, 300, 350);
canvas.drawOval(ovalBounds, paint);
3. Line Shapes
Straight Line:
// Draw single line
canvas.drawLine(startX, startY, stopX, stopY, paint);
// Draw multiple lines
float[] lines = {
x1, y1, x2, y2, // First line
x3, y3, x4, y4 // Second line
};
canvas.drawLines(lines, paint);
Polyline (Connected Lines):
// Draw connected lines using Path
Path polyline = new Path();
polyline.moveTo(100, 400);
polyline.lineTo(150, 350);
polyline.lineTo(200, 380);
polyline.lineTo(250, 340);
canvas.drawPath(polyline, paint);
4. Arc and Sector Shapes
Arc:
// Draw arc (portion of circle/oval)
RectF arcBounds = new RectF(50, 450, 200, 600);
canvas.drawArc(arcBounds, startAngle, sweepAngle, useCenter, paint);
// Examples
canvas.drawArc(arcBounds, 0, 90, false, paint); // Quarter arc
canvas.drawArc(arcBounds, 0, 180, true, paint); // Half pie
5. Custom Shapes using Path
Triangle:
Path triangle = new Path();
triangle.moveTo(150, 100); // Top point
triangle.lineTo(100, 200); // Bottom left
triangle.lineTo(200, 200); // Bottom right
triangle.close(); // Close the path
canvas.drawPath(triangle, paint);
Star Shape:
Path star = new Path();
float centerX = 200, centerY = 300;
float outerRadius = 50, innerRadius = 25;
for (int i = 0; i < 10; i++) {
float angle = (float) (i * Math.PI / 5);
float radius = (i % 2 == 0) ? outerRadius : innerRadius;
float x = centerX + (float) (radius * Math.cos(angle));
float y = centerY + (float) (radius * Math.sin(angle));
if (i == 0) {
star.moveTo(x, y);
} else {
star.lineTo(x, y);
}
}
star.close();
canvas.drawPath(star, paint);
6. Complex Shapes
Bezier Curves:
Path bezier = new Path();
bezier.moveTo(50, 500);
bezier.quadTo(150, 400, 250, 500); // Quadratic Bezier
// or
bezier.cubicTo(cp1x, cp1y, cp2x, cp2y, endX, endY); // Cubic Bezier
canvas.drawPath(bezier, paint);
Complete Implementation Example
package com.example.canvasshapes;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
public class ShapesDrawingView extends View {
private Paint fillPaint, strokePaint, textPaint;
public ShapesDrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
initPaints();
}
private void initPaints() {
// Fill paint
fillPaint = new Paint();
fillPaint.setStyle(Paint.Style.FILL);
fillPaint.setAntiAlias(true);
// Stroke paint
strokePaint = new Paint();
strokePaint.setStyle(Paint.Style.STROKE);
strokePaint.setStrokeWidth(4);
strokePaint.setAntiAlias(true);
// Text paint
textPaint = new Paint();
textPaint.setColor(Color.BLACK);
textPaint.setTextSize(24);
textPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
drawBasicShapes(canvas);
drawAdvancedShapes(canvas);
drawCustomShapes(canvas);
}
private void drawBasicShapes(Canvas canvas) {
// Rectangle
fillPaint.setColor(Color.RED);
canvas.drawRect(50, 50, 200, 150, fillPaint);
canvas.drawText("Rectangle", 60, 180, textPaint);
// Circle
fillPaint.setColor(Color.BLUE);
canvas.drawCircle(300, 100, 50, fillPaint);
canvas.drawText("Circle", 270, 180, textPaint);
// Line
strokePaint.setColor(Color.GREEN);
canvas.drawLine(450, 50, 550, 150, strokePaint);
canvas.drawText("Line", 480, 180, textPaint);
}
private void drawAdvancedShapes(Canvas canvas) {
// Rounded Rectangle
fillPaint.setColor(Color.MAGENTA);
RectF roundRect = new RectF(50, 220, 200, 320);
canvas.drawRoundRect(roundRect, 20, 20, fillPaint);
canvas.drawText("Rounded Rect", 60, 350, textPaint);
// Oval
fillPaint.setColor(Color.CYAN);
RectF oval = new RectF(250, 220, 400, 320);
canvas.drawOval(oval, fillPaint);
canvas.drawText("Oval", 300, 350, textPaint);
// Arc
strokePaint.setColor(Color.YELLOW);
strokePaint.setStrokeWidth(8);
RectF arcRect = new RectF(450, 220, 550, 320);
canvas.drawArc(arcRect, 0, 270, false, strokePaint);
canvas.drawText("Arc", 480, 350, textPaint);
}
private void drawCustomShapes(Canvas canvas) {
// Triangle
fillPaint.setColor(Color.parseColor("#FF6B35"));
Path triangle = new Path();
triangle.moveTo(125, 400);
triangle.lineTo(75, 500);
triangle.lineTo(175, 500);
triangle.close();
canvas.drawPath(triangle, fillPaint);
canvas.drawText("Triangle", 100, 530, textPaint);
// Star
fillPaint.setColor(Color.parseColor("#F7931E"));
Path star = createStarPath(325, 450, 30, 15);
canvas.drawPath(star, fillPaint);
canvas.drawText("Star", 310, 530, textPaint);
// Hexagon
fillPaint.setColor(Color.parseColor("#C1272D"));
Path hexagon = createHexagonPath(500, 450, 40);
canvas.drawPath(hexagon, fillPaint);
canvas.drawText("Hexagon", 470, 530, textPaint);
}
private Path createStarPath(float centerX, float centerY, float outerRadius, float innerRadius) {
Path star = new Path();
double angle = Math.PI / 5;
for (int i = 0; i < 10; i++) {
double currentAngle = i * angle - Math.PI / 2;
float radius = (i % 2 == 0) ? outerRadius : innerRadius;
float x = centerX + (float) (radius * Math.cos(currentAngle));
float y = centerY + (float) (radius * Math.sin(currentAngle));
if (i == 0) {
star.moveTo(x, y);
} else {
star.lineTo(x, y);
}
}
star.close();
return star;
}
private Path createHexagonPath(float centerX, float centerY, float radius) {
Path hexagon = new Path();
for (int i = 0; i < 6; i++) {
double angle = i * Math.PI / 3;
float x = centerX + (float) (radius * Math.cos(angle));
float y = centerY + (float) (radius * Math.sin(angle));
if (i == 0) {
hexagon.moveTo(x, y);
} else {
hexagon.lineTo(x, y);
}
}
hexagon.close();
return hexagon;
}
}
Shape Properties and Customization
Property | Description | Code Example |
---|---|---|
Fill | Solid color fill | paint.setStyle(Paint.Style.FILL) |
Stroke | Outline only | paint.setStyle(Paint.Style.STROKE) |
Fill and Stroke | Both fill and outline | paint.setStyle(Paint.Style.FILL_AND_STROKE) |
Color | Shape color | paint.setColor(Color.RED) |
Alpha | Transparency | paint.setAlpha(128) |
Stroke Width | Line thickness | paint.setStrokeWidth(5) |
Answer B: SQLite Database Closing and Deletion
Understanding Database Lifecycle
Proper management of SQLite databases includes not only creation and usage but also appropriate closing and deletion procedures.
Database Closing Operations
1. Closing Database Connections
Basic Database Closing:
public class DatabaseManager {
private SQLiteDatabase database;
private SQLiteOpenHelper dbHelper;
// Method to close database connection
public void closeDatabase() {
if (database != null && database.isOpen()) {
database.close();
database = null;
}
}
// Method to close database helper
public void closeDatabaseHelper() {
if (dbHelper != null) {
dbHelper.close();
dbHelper = null;
}
}
// Complete cleanup method
public void cleanup() {
closeDatabase();
closeDatabaseHelper();
}
}
2. Cursor Management
public void closeCursorAndDatabase() {
Cursor cursor = null;
SQLiteDatabase db = null;
try {
db = dbHelper.getReadableDatabase();
cursor = db.rawQuery("SELECT * FROM users", null);
// Process cursor data
while (cursor.moveToNext()) {
// Handle data
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// Always close cursor first
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
// Then close database
if (db != null && db.isOpen()) {
db.close();
}
}
}
3. Activity Lifecycle Integration
public class DatabaseActivity extends Activity {
private DatabaseHelper dbHelper;
private SQLiteDatabase database;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dbHelper = new DatabaseHelper(this);
database = dbHelper.getWritableDatabase();
}
@Override
protected void onPause() {
super.onPause();
// Close database when activity is paused
if (database != null && database.isOpen()) {
database.close();
}
}
@Override
protected void onResume() {
super.onResume();
// Reopen database when activity resumes
if (database == null || !database.isOpen()) {
database = dbHelper.getWritableDatabase();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// Final cleanup
if (database != null && database.isOpen()) {
database.close();
}
if (dbHelper != null) {
dbHelper.close();
}
}
}
Database Deletion Operations
1. Deleting Database File
Complete Database Deletion:
public class DatabaseDeletionManager {
private Context context;
private String databaseName;
public DatabaseDeletionManager(Context context, String databaseName) {
this.context = context;
this.databaseName = databaseName;
}
public boolean deleteDatabase() {
try {
// Method 1: Using context.deleteDatabase()
boolean deleted = context.deleteDatabase(databaseName);
if (deleted) {
Log.d("Database", "Database deleted successfully");
return true;
} else {
Log.e("Database", "Failed to delete database");
return false;
}
} catch (Exception e) {
Log.e("Database", "Error deleting database: " + e.getMessage());
return false;
}
}
public boolean deleteDatabaseManually() {
try {
// Method 2: Manual file deletion
File databaseFile = context.getDatabasePath(databaseName);
if (databaseFile.exists()) {
boolean deleted = databaseFile.delete();
// Also delete associated files
File walFile = new File(databaseFile.getPath() + "-wal");
File shmFile = new File(databaseFile.getPath() + "-shm");
if (walFile.exists()) walFile.delete();
if (shmFile.exists()) shmFile.delete();
return deleted;
}
return true; // Database doesn't exist
} catch (Exception e) {
Log.e("Database", "Error in manual deletion: " + e.getMessage());
return false;
}
}
}
2. Conditional Database Deletion
public class ConditionalDatabaseManager {
public void deleteOldDatabases(Context context) {
// Get all database files
File databaseDir = new File(context.getApplicationInfo().dataDir + "/databases/");
if (databaseDir.exists() && databaseDir.isDirectory()) {
File[] files = databaseDir.listFiles();
if (files != null) {
for (File file : files) {
// Delete databases older than 30 days
long fileAge = System.currentTimeMillis() - file.lastModified();
long thirtyDaysInMillis = 30 * 24 * 60 * 60 * 1000L;
if (fileAge > thirtyDaysInMillis && file.getName().endsWith(".db")) {
boolean deleted = file.delete();
Log.d("Database", "Old database " + file.getName() +
(deleted ? " deleted" : " deletion failed"));
}
}
}
}
}
public void deleteEmptyDatabases(Context context) {
String[] databaseNames = {"temp_db", "cache_db", "backup_db"};
for (String dbName : databaseNames) {
if (isDatabaseEmpty(context, dbName)) {
boolean deleted = context.deleteDatabase(dbName);
Log.d("Database", "Empty database " + dbName +
(deleted ? " deleted" : " deletion failed"));
}
}
}
private boolean isDatabaseEmpty(Context context, String databaseName) {
SQLiteDatabase db = null;
Cursor cursor = null;
try {
db = context.openOrCreateDatabase(databaseName, Context.MODE_PRIVATE, null);
// Check if any tables exist
cursor = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'", null);
// If no tables or only sqlite_sequence table, consider empty
return cursor.getCount() <= 1;
} catch (Exception e) {
return false;
} finally {
if (cursor != null) cursor.close();
if (db != null) db.close();
}
}
}
3. Safe Database Operations with Transactions
public class SafeDatabaseOperations {
public boolean safeDeleteAndRecreate(Context context, String databaseName) {
SQLiteDatabase db = null;
try {
// Begin transaction
db = context.openOrCreateDatabase(databaseName, Context.MODE_PRIVATE, null);
db.beginTransaction();
// Backup important data if needed
backupCriticalData(db);
// Close database before deletion
db.setTransactionSuccessful();
db.endTransaction();
db.close();
db = null;
// Delete database
boolean deleted = context.deleteDatabase(databaseName);
if (deleted) {
// Recreate database with fresh schema
recreateDatabase(context, databaseName);
return true;
}
return false;
} catch (Exception e) {
if (db != null) {
if (db.inTransaction()) {
db.endTransaction();
}
db.close();
}
Log.e("Database", "Error in safe delete and recreate: " + e.getMessage());
return false;
}
}
private void backupCriticalData(SQLiteDatabase db) {
// Implement backup logic for critical data
Cursor cursor = db.rawQuery("SELECT * FROM critical_table", null);
// Store data in SharedPreferences or external file
SharedPreferences prefs = context.getSharedPreferences("db_backup", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
while (cursor.moveToNext()) {
// Save critical data
String key = cursor.getString(cursor.getColumnIndex("key"));
String value = cursor.getString(cursor.getColumnIndex("value"));
editor.putString(key, value);
}
editor.apply();
cursor.close();
}
private void recreateDatabase(Context context, String databaseName) {
DatabaseHelper helper = new DatabaseHelper(context, databaseName);
SQLiteDatabase db = helper.getWritableDatabase();
// Database will be created with fresh schema
// Restore critical data if needed
restoreCriticalData(context, db);
db.close();
helper.close();
}
private void restoreCriticalData(Context context, SQLiteDatabase db) {
SharedPreferences prefs = context.getSharedPreferences("db_backup", Context.MODE_PRIVATE);
Map<String, ?> backupData = prefs.getAll();
for (Map.Entry<String, ?> entry : backupData.entrySet()) {
ContentValues values = new ContentValues();
values.put("key", entry.getKey());
values.put("value", entry.getValue().toString());
db.insert("critical_table", null, values);
}
// Clear backup data
prefs.edit().clear().apply();
}
}
Best Practices Summary
Database Closing Best Practices:
- Always close cursors before closing databases
- Use try-catch-finally blocks for safe closing
- Close in reverse order: Cursor → Database → Helper
- Check if resources are null and open before closing
- Integrate with activity lifecycle properly
Database Deletion Best Practices:
- Close all connections before deletion
- Backup critical data before deletion
- Use context.deleteDatabase() for proper cleanup
- Handle associated files (WAL, SHM)
- Verify deletion success
- Consider user data protection regulations
Complete Example Implementation
public class CompleteDatabaseManager {
private Context context;
private SQLiteOpenHelper dbHelper;
private SQLiteDatabase database;
public CompleteDatabaseManager(Context context) {
this.context = context;
this.dbHelper = new DatabaseHelper(context);
}
public void openDatabase() {
if (database == null || !database.isOpen()) {
database = dbHelper.getWritableDatabase();
}
}
public void closeDatabase() {
if (database != null && database.isOpen()) {
database.close();
database = null;
}
}
public boolean deleteDatabase(String databaseName) {
try {
// Close current connections
closeDatabase();
if (dbHelper != null) {
dbHelper.close();
dbHelper = null;
}
// Delete database
boolean deleted = context.deleteDatabase(databaseName);
// Reinitialize if needed
dbHelper = new DatabaseHelper(context);
return deleted;
} catch (Exception e) {
Log.e("DatabaseManager", "Error deleting database: " + e.getMessage());
return false;
}
}
public void cleanup() {
closeDatabase();
if (dbHelper != null) {
dbHelper.close();
dbHelper = null;
}
}
}