Cara buat aplikasi pembuat kupon snack atau makanan dengan menggunakan android studio

Table of Contents

Cara buat aplikasi pembuat kupon snack atau makanan dengan menggunakan android studio




kode projek kupon maker

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 ?

1. Memiliki fitur milih tema berdasarkan selera dengan kurang lebih 30 tema
2. Tersedia custom nama even dan tujuan pembuatan kupon
3. Bisa membuat lebih dari 100 kupon dalam waktu 1 menit

Kekurangan aplikasi kupon ini ?

Kendala yang dihadapin adalah jika menggunakan android lawas biasanya akses akan ditolak olah sebab itu agar aplikasi ini bekerja maka gunakanlah android terbaru, sebelumnya sudah di uji coba menggunakan tablet huawei dan hasilnya akses ditolak.
 

Kelebihan aplikasi kupon ini?

1. Bisa dipakai tanpa menggunakan koneksi internet
2. Format file dalam bentuk pdf siap cetak
3. Ukuran menggunakan kertas A4
4. Tersedia Fitur Share pada aplikasi


Proses pembuatan aplikasi ?

1. AndroidManifest

Pada bagian ini silahkan kamu copy paste saja kode berikut ini:

<?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 

Pada posisi ini sobat bisa lihat desain layout yang dipakai pada aplikasi dimana untuk tampilan dan kodenya kurang lebih seperti ini:

desain aplikasi

5. Kode Layout


<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background">

<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/primary">

<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="Kertashitam Voucher"
app:titleTextColor="@color/white" />

</com.google.android.material.appbar.AppBarLayout>

<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">

<!-- Welcome Card -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardElevation="2dp"
app:strokeWidth="0dp"
android:layout_marginBottom="16dp">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="20dp"
android:gravity="center_vertical">

<TextView
android:layout_width="60dp"
android:layout_height="60dp"
android:text="🎫"
android:textSize="40sp"
android:gravity="center"
android:background="#F0F0F0"
tools:ignore="HardcodedText" />

<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginStart="16dp">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Selamat Datang"
android:textSize="14sp"
android:textColor="#FF5722"
tools:ignore="HardcodedText" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Voucher Generator"
android:textColor="#FF5722"
android:textSize="20sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

<!-- Preview Card -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardElevation="4dp"
app:strokeColor="#E0E0E0"
app:strokeWidth="1dp"
android:layout_marginBottom="16dp">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="LIVE PREVIEW"
android:textSize="12sp"
android:textStyle="bold"
android:textColor="#FF5722"
android:layout_marginBottom="12dp"
android:letterSpacing="0.1"
tools:ignore="HardcodedText" />

<FrameLayout
android:layout_width="match_parent"
android:layout_height="220dp"
android:background="#F5F5F5"
android:padding="8dp">
<ImageView
android:id="@+id/ivPreview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
tools:ignore="ContentDescription" />
</FrameLayout>

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/menuModel"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="Template Desain"
tools:ignore="HardcodedText">
<AutoCompleteTextView
android:id="@+id/autoCompleteModel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
android:textSize="14sp"
tools:ignore="LabelFor" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

<!-- Form Card -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardElevation="4dp"
app:strokeWidth="0dp"
android:layout_marginBottom="16dp">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">

<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Nama Event"
tools:ignore="HardcodedText">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etEvent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textCapCharacters" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:hint="Tujuan"
tools:ignore="HardcodedText">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textCapCharacters" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:hint="Jumlah Voucher"
tools:ignore="HardcodedText">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etJumlah"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.button.MaterialButton
android:id="@+id/btnGenerate"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginTop="24dp"
android:text="GENERATE PDF"
android:textSize="16sp"
android:textStyle="bold"
app:cornerRadius="12dp"
android:backgroundTint="#FF5722"
tools:ignore="HardcodedText" />

</LinearLayout>
</com.google.android.material.card.MaterialCardView>

<!-- Info Card -->
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:strokeWidth="1dp"
app:strokeColor="#EEEEEE"
android:layout_marginBottom="24dp">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Spesifikasi Cetak"
android:textStyle="bold"
android:textColor="@color/primary"
tools:ignore="HardcodedText" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="• Ukuran Kertas: A4 Portrait\n• Tata Letak: 3 Kolom x 8 Baris (24 Voucher/Lembar)\n• Font: Arial Profesional\n• Output: PDF High Quality"
android:textSize="12sp"
android:textColor="#666666"
android:lineSpacingExtra="4dp"
tools:ignore="HardcodedText" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

</LinearLayout>
</androidx.core.widget.NestedScrollView>

</androidx.coordinatorlayout.widget.CoordinatorLayout>


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>

KESIMPULAN 


Tujuan  pembuatan aplikasi ini adalah untuk mempermudah kamu dalam membuat kupon sederhana tanpa makan waktu yang banyak, kamu bisa buat 100 kupon hanya dalam 1 menit, semoga artikel ini bermanfaat sampai bertemu di kesempatan lainnya.

Penulis

Posting Komentar