Cara buat aplikasi pembuat kupon snack atau makanan dengan menggunakan android studio
Cara buat aplikasi pembuat kupon snack atau makanan dengan menggunakan android studio
Apakah kupon itu ?
pada dasarnya kupon ini adalah media yang digunakan sebagai pemberitahuaan atau tagging dipakai berbagai acara indoor dan outdoor seperti komunitas tuna daksa atau komunitas sepeda jadoel atau acara santunan anank yatim atau acara besar lainnya.
Seperti sobat ketahui, membuat kupon dalam jumlah besar pastinya sangat melelahkan apalagi kita yang kerjanya masih manual satu persatu sudah pasti butuh waktu yang lama, nah bagaimana konsepnya jika kita buat kupon ini modal 1 sampai 5 menit aja sudah jadi dan desainnya bisa kita pilih menyesuaikan.
Fitur apa saja yang ada dalam konsep ini ?
Kekurangan aplikasi kupon ini ?
Kelebihan aplikasi kupon ini?
Proses pembuatan aplikasi ?
1. AndroidManifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission
android:name="android.permission.READ_MEDIA_IMAGES"
tools:ignore="PhotoAndVideoPolicy,SelectedPhotoAccess" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@drawable/cuponapk"
android:label="@string/app_name"
android:roundIcon="@drawable/cuponapk"
android:supportsRtl="true"
android:theme="@style/Theme.VoucherKu"
android:requestLegacyExternalStorage="true">
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:exported="true"
android:screenOrientation="fullSensor"
tools:ignore="DiscouragedApi">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>
2. MainActivity
Selanjutnya kita masuk kebagian java code dimana untuk settingan awal bisa kamu pastekan kode berikut ini:
package com.cuponmaker;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import java.io.File;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private EditText etEvent;
private EditText etMenu;
private EditText etJumlah;
private Button btnGenerate;
private ImageView ivPreview;
private AutoCompleteTextView autoCompleteModel;
private PdfGenerator.DesignModel selectedModel = PdfGenerator.DesignModel.EXCLUSIVE;
private static final int STORAGE_PERMISSION_CODE = 100;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
initViews();
checkPermission();
btnGenerate.setOnClickListener(v -> generateVoucher());
setupDesignSelection();
setupLivePreview();
updatePreview();
}
private void initViews() {
etEvent = findViewById(R.id.etEvent);
etMenu = findViewById(R.id.etMenu);
etJumlah = findViewById(R.id.etJumlah);
btnGenerate = findViewById(R.id.btnGenerate);
ivPreview = findViewById(R.id.ivPreview);
autoCompleteModel = findViewById(R.id.autoCompleteModel);
}
private void setupDesignSelection() {
String[] models = {
"Exclusive Gold", "Modern Blue", "Vintage Brown", "Minimalist Gray",
"Elegant Dark", "Eco Green", "Party Red", "Corporate Silver",
"Royal Purple", "Sunset Orange", "Luxury Black", "Midnight Blue",
"Coffee Time", "Cherry Blossom", "Ocean Breeze", "Cyberpunk Neon",
"Pastel Dream", "Industrial Yellow", "Kids Zone", "Gaming Red",
"Golden Ticket", "Retro Cassette", "Space Nebula", "Bamboo Zen",
"Marble White", "Disco Night", "Safari Jungle", "Tech Grid",
"Candy Shop", "Vampire Dark"
};
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, models);
autoCompleteModel.setAdapter(adapter);
autoCompleteModel.setText(models[0], false);
autoCompleteModel.setOnItemClickListener((parent, view, position, id) -> {
selectedModel = PdfGenerator.DesignModel.values()[position];
updatePreview();
});
}
private void setupLivePreview() {
TextWatcher watcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
updatePreview();
}
};
etEvent.addTextChangedListener(watcher);
etMenu.addTextChangedListener(watcher);
}
private void updatePreview() {
String event = etEvent.getText().toString();
String menu = etMenu.getText().toString();
if (event.isEmpty()) event = "NAMA EVENT";
if (menu.isEmpty()) menu = "NAMA MENU";
Bitmap preview = PdfGenerator.createPreview(this, event, menu, selectedModel);
ivPreview.setImageBitmap(preview);
}
private void generateVoucher() {
String eventName = etEvent.getText().toString().trim();
String menu = etMenu.getText().toString().trim();
String jumlahText = etJumlah.getText().toString().trim();
if (eventName.isEmpty()) {
etEvent.setError("Nama event wajib diisi");
etEvent.requestFocus();
return;
}
if (menu.isEmpty()) {
etMenu.setError("Jenis voucher wajib diisi");
etMenu.requestFocus();
return;
}
if (jumlahText.isEmpty()) {
etJumlah.setError("Jumlah voucher wajib diisi");
etJumlah.requestFocus();
return;
}
int jumlahVoucher;
try {
jumlahVoucher = Integer.parseInt(jumlahText);
} catch (Exception e) {
Toast.makeText(this,
"Jumlah voucher tidak valid",
Toast.LENGTH_SHORT).show();
return;
}
if (jumlahVoucher <= 0) {
Toast.makeText(this,
"Jumlah voucher harus lebih dari 0",
Toast.LENGTH_SHORT).show();
return;
}
btnGenerate.setEnabled(false);
ArrayList<Voucher> voucherList = new ArrayList<>();
for (int i = 1; i <= jumlahVoucher; i++) {
String nomor = String.format("%04d", i);
voucherList.add(
new Voucher(
nomor,
eventName,
menu
)
);
}
try {
File pdfFile = PdfGenerator.createVoucherPdf(
this,
voucherList,
eventName,
menu,
selectedModel
);
if (pdfFile != null) {
Toast.makeText(
this,
"PDF berhasil dibuat:\n" + pdfFile.getAbsolutePath(),
Toast.LENGTH_LONG
).show();
openPdf(pdfFile);
} else {
Toast.makeText(
this,
"Gagal membuat PDF",
Toast.LENGTH_SHORT
).show();
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(
this,
"Error: " + e.getMessage(),
Toast.LENGTH_LONG
).show();
}
btnGenerate.setEnabled(true);
}
private void openPdf(File file) {
try {
Uri uri = FileProvider.getUriForFile(
this,
getPackageName() + ".fileprovider",
file
);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "application/pdf");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} catch (Exception e) {
Toast.makeText(this,
"Tidak ada aplikasi untuk membuka PDF",
Toast.LENGTH_SHORT).show();
}
}
private void checkPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// Android 11+ (API 30+) doesn't need WRITE_EXTERNAL_STORAGE for its own files in Downloads
// But if it fails, the user might need to grant MANAGE_EXTERNAL_STORAGE (too much)
// or we use MediaStore. For now, we assume it works or falls back.
return;
}
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE
},
STORAGE_PERMISSION_CODE
);
}
}
@Override
public void onRequestPermissionsResult(
int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(
requestCode,
permissions,
grantResults
);
if (requestCode == STORAGE_PERMISSION_CODE) {
if (grantResults.length > 0
&& grantResults[0]
== PackageManager.PERMISSION_GRANTED) {
Toast.makeText(
this,
"Permission diberikan",
Toast.LENGTH_SHORT
).show();
} else {
Toast.makeText(
this,
"Permission ditolak",
Toast.LENGTH_SHORT
).show();
}
}
}
}
3. PdfGenerator
Langkah ini digunakan untuk membuat file hasil generator menjadi file pdf yang siap untuk di print dengan settingan kerat A4 dan bisa di share atau berbagi dengan orang lain berikut kodenya:
package com.cuponmaker;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.pdf.PdfDocument;
import android.media.MediaScannerConnection;
import android.os.Environment;
import java.io.File;
import java.io.FileOutputStream;
import java.util.List;
public class PdfGenerator {
public enum DesignModel {
EXCLUSIVE, MODERN, VINTAGE, MINIMALIST, ELEGANT_DARK,
ECO_GREEN, PARTY_RED, CORPORATE_SILVER, ROYAL_PURPLE, SUNSET_ORANGE,
LUXURY_BLACK, MIDNIGHT_BLUE, COFFEE_TIME, CHERRY_BLOSSOM, OCEAN_BREEZE,
CYBERPUNK, PASTEL_DREAM, INDUSTRIAL, KIDS_ZONE, GAMING_RED,
GOLDEN_TICKET, RETRO_CASSETTE, SPACE_NEBULA, BAMBOO_ZEN, MARBLE_WHITE,
DISCO_NIGHT, SAFARI_JUNGLE, TECH_GRID, CANDY_SHOP, VAMPIRE_DARK
}
public static Bitmap createPreview(Context context, String eventName, String menu, DesignModel model) {
int width = 450;
int height = 250;
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawVoucher(canvas, 0, 0, width, height, "0001", eventName, menu, model);
return bitmap;
}
public static void drawVoucher(Canvas canvas, float left, float top, float right, float bottom,
String nomor, String eventName, String menu, DesignModel model) {
RectF rect = new RectF(left + 8, top + 8, right - 8, bottom - 8);
float w = rect.width();
float h = rect.height();
float cx = rect.centerX();
float cy = rect.centerY();
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
// Arial style (Sans Serif) for all
Typeface arialBold = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
Typeface arialNormal = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
int primaryColor = Color.BLACK;
int bgColor = Color.WHITE;
int accentColor = Color.GRAY;
// Apply Design Templates
switch (model) {
case EXCLUSIVE:
bgColor = Color.parseColor("#FFF8E1");
primaryColor = Color.parseColor("#1A1A1A");
accentColor = Color.parseColor("#B8860B");
p.setColor(bgColor);
canvas.drawRoundRect(rect, 15, 15, p);
p.setStyle(Paint.Style.STROKE);
p.setColor(accentColor);
p.setStrokeWidth(3f);
canvas.drawRoundRect(rect, 15, 15, p);
break;
case MODERN:
accentColor = Color.parseColor("#1976D2");
p.setColor(Color.WHITE);
canvas.drawRect(rect, p);
p.setColor(accentColor);
canvas.drawRect(left + 8, top + 8, left + 25, bottom - 8, p);
break;
case VINTAGE:
bgColor = Color.parseColor("#F4ECD8");
primaryColor = Color.parseColor("#5D4037");
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setStyle(Paint.Style.STROKE);
p.setColor(primaryColor);
p.setStrokeWidth(2f);
canvas.drawRect(rect.left + 10, rect.top + 10, rect.right - 10, rect.bottom - 10, p);
break;
case MINIMALIST:
p.setColor(Color.WHITE);
canvas.drawRect(rect, p);
p.setStyle(Paint.Style.STROKE);
p.setColor(Color.LTGRAY);
p.setStrokeWidth(1f);
canvas.drawRect(rect, p);
primaryColor = Color.DKGRAY;
accentColor = Color.GRAY;
break;
case ELEGANT_DARK:
bgColor = Color.parseColor("#212121");
primaryColor = Color.WHITE;
accentColor = Color.parseColor("#FFD700");
p.setColor(bgColor);
canvas.drawRoundRect(rect, 10, 10, p);
p.setStyle(Paint.Style.STROKE);
p.setColor(accentColor);
p.setStrokeWidth(1.5f);
canvas.drawRoundRect(rect, 10, 10, p);
break;
case ECO_GREEN:
bgColor = Color.parseColor("#F1F8E9");
primaryColor = Color.parseColor("#2E7D32");
accentColor = Color.parseColor("#8BC34A");
p.setColor(bgColor);
canvas.drawRoundRect(rect, 20, 20, p);
p.setStyle(Paint.Style.STROKE);
p.setColor(primaryColor);
p.setStrokeWidth(2.5f);
canvas.drawRoundRect(rect, 20, 20, p);
break;
case PARTY_RED:
bgColor = Color.parseColor("#FFEBEE");
primaryColor = Color.parseColor("#C62828");
accentColor = Color.parseColor("#FF5252");
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setColor(accentColor);
canvas.drawCircle(rect.left, cy, h/4, p);
canvas.drawCircle(rect.right, cy, h/4, p);
break;
case CORPORATE_SILVER:
bgColor = Color.parseColor("#F5F5F5");
primaryColor = Color.parseColor("#37474F");
accentColor = Color.parseColor("#78909C");
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setColor(primaryColor);
canvas.drawRect(rect.left, rect.top, rect.right, rect.top + h*0.2f, p);
break;
case ROYAL_PURPLE:
bgColor = Color.parseColor("#F3E5F5");
primaryColor = Color.parseColor("#4A148C");
accentColor = Color.parseColor("#AB47BC");
p.setColor(bgColor);
canvas.drawRoundRect(rect, 15, 15, p);
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(4f);
p.setColor(primaryColor);
canvas.drawRoundRect(rect, 15, 15, p);
break;
case SUNSET_ORANGE:
bgColor = Color.parseColor("#FFF3E0");
primaryColor = Color.parseColor("#E65100");
accentColor = Color.parseColor("#FF9800");
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setColor(accentColor);
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(10f);
canvas.drawLine(rect.left, rect.bottom, rect.right, rect.bottom, p);
break;
case LUXURY_BLACK:
bgColor = Color.parseColor("#000000");
primaryColor = Color.parseColor("#D4AF37"); // Gold
accentColor = Color.parseColor("#C0C0C0"); // Silver
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setStyle(Paint.Style.STROKE);
p.setColor(primaryColor);
p.setStrokeWidth(4f);
canvas.drawRect(rect.left + 5, rect.top + 5, rect.right - 5, rect.bottom - 5, p);
break;
case MIDNIGHT_BLUE:
bgColor = Color.parseColor("#191970");
primaryColor = Color.WHITE;
accentColor = Color.parseColor("#ADD8E6");
p.setColor(bgColor);
canvas.drawRoundRect(rect, 10, 10, p);
break;
case COFFEE_TIME:
bgColor = Color.parseColor("#D2B48C"); // Tan
primaryColor = Color.parseColor("#3E2723"); // Dark Brown
accentColor = Color.parseColor("#795548");
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setStyle(Paint.Style.STROKE);
p.setColor(primaryColor);
p.setStrokeWidth(2f);
p.setPathEffect(new DashPathEffect(new float[]{10, 5}, 0));
canvas.drawRect(rect, p);
p.setPathEffect(null);
break;
case CHERRY_BLOSSOM:
bgColor = Color.parseColor("#FFF0F5");
primaryColor = Color.parseColor("#DB7093");
accentColor = Color.parseColor("#FFB6C1");
p.setColor(bgColor);
canvas.drawRoundRect(rect, 25, 25, p);
break;
case OCEAN_BREEZE:
bgColor = Color.parseColor("#E0F7FA");
primaryColor = Color.parseColor("#006064");
accentColor = Color.parseColor("#26C6DA");
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setColor(accentColor);
canvas.drawRect(rect.left, rect.bottom - 20, rect.right, rect.bottom, p);
break;
case CYBERPUNK:
bgColor = Color.parseColor("#2B2B2B");
primaryColor = Color.parseColor("#00FF00"); // Neon Green
accentColor = Color.parseColor("#FF00FF"); // Neon Pink
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setStyle(Paint.Style.STROKE);
p.setColor(primaryColor);
canvas.drawLine(rect.left, rect.top, rect.left + 30, rect.top, p);
canvas.drawLine(rect.right - 30, rect.bottom, rect.right, rect.bottom, p);
break;
case PASTEL_DREAM:
bgColor = Color.parseColor("#F3E5F5");
primaryColor = Color.parseColor("#7E57C2");
accentColor = Color.parseColor("#B39DDB");
p.setColor(bgColor);
canvas.drawRoundRect(rect, 30, 30, p);
break;
case INDUSTRIAL:
bgColor = Color.parseColor("#B0BEC5");
primaryColor = Color.parseColor("#263238");
accentColor = Color.parseColor("#FFB300"); // Safety Orange/Yellow
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setColor(accentColor);
canvas.drawRect(rect.left, rect.top, rect.left + 15, rect.top + 15, p);
break;
case KIDS_ZONE:
bgColor = Color.parseColor("#FFF9C4"); // Light Yellow
primaryColor = Color.parseColor("#F44336"); // Red
accentColor = Color.parseColor("#2196F3"); // Blue
p.setColor(bgColor);
canvas.drawRoundRect(rect, 20, 20, p);
break;
case GAMING_RED:
bgColor = Color.parseColor("#121212");
primaryColor = Color.parseColor("#D50000");
accentColor = Color.WHITE;
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setColor(primaryColor);
canvas.drawLine(rect.left, rect.top + 10, rect.right, rect.top + 10, p);
canvas.drawLine(rect.left, rect.bottom - 10, rect.right, rect.bottom - 10, p);
break;
case GOLDEN_TICKET:
bgColor = Color.parseColor("#FFD700"); // Real Gold
primaryColor = Color.parseColor("#5D4037"); // Chocolate Brown
accentColor = Color.parseColor("#8B4513");
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(5f);
p.setColor(primaryColor);
canvas.drawRect(rect.left + 10, rect.top + 10, rect.right - 10, rect.bottom - 10, p);
break;
case RETRO_CASSETTE:
bgColor = Color.parseColor("#EEEEEE");
primaryColor = Color.parseColor("#E91E63"); // Pink
accentColor = Color.parseColor("#00BCD4"); // Cyan
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setColor(accentColor);
canvas.drawRect(rect.left, rect.top, rect.right, rect.top + 15, p);
p.setColor(primaryColor);
canvas.drawRect(rect.left, rect.bottom - 15, rect.right, rect.bottom, p);
break;
case SPACE_NEBULA:
bgColor = Color.parseColor("#0D0221"); // Deep Space
primaryColor = Color.parseColor("#00F5FF"); // Electric Blue
accentColor = Color.parseColor("#7A28FF"); // Purple
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setColor(Color.WHITE);
for(int i=0; i<5; i++) canvas.drawCircle(rect.left + (w * (float)Math.random()), rect.top + (h * (float)Math.random()), 1f, p);
break;
case BAMBOO_ZEN:
bgColor = Color.parseColor("#F5F5DC"); // Beige
primaryColor = Color.parseColor("#33691E"); // Dark Green
accentColor = Color.parseColor("#8BC34A"); // Light Green
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setColor(primaryColor);
canvas.drawRect(rect.left, rect.top, rect.left + 10, rect.bottom, p);
break;
case MARBLE_WHITE:
bgColor = Color.WHITE;
primaryColor = Color.parseColor("#424242");
accentColor = Color.parseColor("#BDBDBD");
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setColor(accentColor);
p.setStrokeWidth(1f);
canvas.drawLine(rect.left, rect.top, rect.right, rect.bottom, p);
canvas.drawLine(rect.right, rect.top, rect.left, rect.bottom, p);
break;
case DISCO_NIGHT:
bgColor = Color.parseColor("#212121");
primaryColor = Color.parseColor("#FFC107"); // Yellow
accentColor = Color.parseColor("#9C27B0"); // Purple
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setColor(primaryColor);
canvas.drawCircle(rect.left + 20, rect.top + 20, 10, p);
p.setColor(accentColor);
canvas.drawCircle(rect.right - 20, rect.bottom - 20, 10, p);
break;
case SAFARI_JUNGLE:
bgColor = Color.parseColor("#E6EE9C"); // Lime Light
primaryColor = Color.parseColor("#1B5E20"); // Forest Green
accentColor = Color.parseColor("#795548"); // Brown
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setColor(primaryColor);
canvas.drawRect(rect.left, rect.top, rect.right, rect.top + 10, p);
break;
case TECH_GRID:
bgColor = Color.parseColor("#1A237E"); // Navy
primaryColor = Color.parseColor("#00E5FF"); // Cyan
accentColor = Color.WHITE;
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setColor(primaryColor);
p.setAlpha(50);
for(int i=0; i<w; i+=40) canvas.drawLine(rect.left + i, rect.top, rect.left + i, rect.bottom, p);
p.setAlpha(255);
break;
case CANDY_SHOP:
bgColor = Color.parseColor("#FFF3E0");
primaryColor = Color.parseColor("#FF4081"); // Pink
accentColor = Color.parseColor("#448AFF"); // Blue
p.setColor(bgColor);
canvas.drawRoundRect(rect, 15, 15, p);
p.setColor(primaryColor);
canvas.drawCircle(rect.left, rect.top, 30, p);
break;
case VAMPIRE_DARK:
bgColor = Color.BLACK;
primaryColor = Color.parseColor("#B71C1C"); // Blood Red
accentColor = Color.WHITE;
p.setColor(bgColor);
canvas.drawRect(rect, p);
p.setColor(primaryColor);
canvas.drawRect(rect.left, rect.top, rect.right, rect.top + 5, p);
canvas.drawRect(rect.left, rect.bottom - 5, rect.right, rect.bottom, p);
break;
}
// DRAW CONTENT
p.setStyle(Paint.Style.FILL);
p.setTextAlign(Paint.Align.CENTER);
// Header / Brand - Arial (Diturunkan sesuai permintaan agar lebih rapi)
p.setColor((model == DesignModel.CORPORATE_SILVER) ? Color.WHITE : primaryColor);
p.setTypeface(arialBold);
p.setTextSize(h * 0.12f);
canvas.drawText("", cx, top + h * 0.32f, p);
// Event Name - Arial (Ikut turun menjaga kerapatan)
p.setColor(accentColor);
p.setTextSize(h * 0.08f);
canvas.drawText(eventName.toUpperCase(), cx, top + h * 0.42f, p);
// Main Menu - Arial (Auto-scale agar rapi & tidak keluar batas)
p.setColor(primaryColor);
p.setTypeface(arialBold);
float baseMenuSize = h * 0.18f;
p.setTextSize(baseMenuSize);
float maxWidth = w - 20; // Margin aman kiri-kanan
float textWidth = p.measureText(menu);
if (textWidth > maxWidth) {
p.setTextSize(baseMenuSize * (maxWidth / textWidth));
}
canvas.drawText(menu, cx, top + h * 0.62f, p);
// Serial Number - Arial (Dibuat Besar & Warna Biru Tua)
p.setColor(Color.parseColor("#000080")); // Navy Blue (Biru Tua)
p.setTypeface(arialBold);
p.setTextSize(h * 0.22f); // Ukuran besar tetap dipertahankan
canvas.drawText("#" + nomor, cx, top + h * 0.82f, p);
// Footer small text - Arial (Sedikit penyesuaian posisi)
p.setColor(accentColor);
p.setTypeface(arialNormal);
p.setTextSize(h * 0.05f);
canvas.drawText("Valid for 1 person • Non-refundable", cx, top + h * 0.94f, p);
}
public static File createVoucherPdf(
Context context,
List<Voucher> vouchers,
String eventName,
String menu,
DesignModel model) {
PdfDocument document = new PdfDocument();
final int PAGE_WIDTH = 595; // A4
final int PAGE_HEIGHT = 842;
// Satu lembar dibuat 3 kolom x 8 baris (24 voucher)
int cols = 3;
int rows = 8;
int vWidth = PAGE_WIDTH / cols;
int vHeight = PAGE_HEIGHT / rows;
int perPage = cols * rows;
int totalPages = (int) Math.ceil(vouchers.size() / (double) perPage);
int index = 0;
for (int pNum = 0; pNum < totalPages; pNum++) {
PdfDocument.PageInfo pi = new PdfDocument.PageInfo.Builder(PAGE_WIDTH, PAGE_HEIGHT, pNum + 1).create();
PdfDocument.Page page = document.startPage(pi);
Canvas canvas = page.getCanvas();
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (index >= vouchers.size()) break;
// Margin dibuat lebih rapat (5) agar ruang voucher lebih maksimal
float margin = 5;
drawVoucher(canvas,
(c * vWidth) + margin,
(r * vHeight) + margin,
((c + 1) * vWidth) - margin,
((r + 1) * vHeight) - margin,
vouchers.get(index).getNomor(), eventName, menu, model);
index++;
}
}
document.finishPage(page);
}
try {
String fileName =
"Voucher_" +
System.currentTimeMillis() +
".pdf";
File downloadsDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS);
if (!downloadsDir.exists()) {
downloadsDir.mkdirs();
}
File file = new File(downloadsDir, fileName);
FileOutputStream fos = new FileOutputStream(file);
document.writeTo(fos);
document.close();
fos.close();
// Notify MediaScanner so it appears in file managers
MediaScannerConnection.scanFile(
context,
new String[]{file.getAbsolutePath()},
new String[]{"application/pdf"},
(path, uri) -> {}
);
return file;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
4. Voucher
pada bagian ini adalah untuk settingan atau custom voucher seperti nama even tujuan kupon dan jumlah voucher yang akan dibuat untuk kodenya sendiri bisa kamu paste kode dibawah ini:
package com.cuponmaker;
public class Voucher {
private String nomor;
private String eventName;
private String menu;
public Voucher(String nomor, String eventName, String menu) {
this.nomor = nomor;
this.eventName = eventName;
this.menu = menu;
}
public String getNomor() {
return nomor;
}
public void setNomor(String nomor) {
this.nomor = nomor;
}
public String getEventName() {
return eventName;
}
public void setEventName(String eventName) {
this.eventName = eventName;
}
public String getMenu() {
return menu;
}
public void setMenu(String menu) {
this.menu = menu;
}
@Override
public String toString() {
return "Voucher{" +
"nomor='" + nomor + '\'' +
", eventName='" + eventName + '\'' +
", menu='" + menu + '\'' +
'}';
}
}
Desain untuk layout
6. Color
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="primary">#1A1A1A</color>
<color name="primary_variant">#000000</color>
<color name="secondary">#000080</color>
<color name="secondary_variant">#000050</color>
<color name="background">#F8F9FA</color>
<color name="surface">#FFFFFF</color>
<color name="on_primary">#FFFFFF</color>
<color name="on_secondary">#FFFFFF</color>
<color name="on_background">#1A1A1A</color>
<color name="on_surface">#1A1A1A</color>
<color name="accent_gold">#B8860B</color>
<color name="white">#FFFFFF</color>
<color name="black">#000000</color>
</resources>
Posting Komentar