UI Cetak ulang struk

This commit is contained in:
riz081 2025-06-05 13:03:44 +07:00
parent a30e767adc
commit 5a03fc3aec
6 changed files with 296 additions and 85 deletions

View File

@ -2,15 +2,20 @@ package com.example.bdkipoc;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.json.JSONArray;
import org.json.JSONException;
@ -26,66 +31,169 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class TransactionActivity extends AppCompatActivity {
public class TransactionActivity extends AppCompatActivity implements TransactionAdapter.OnPrintClickListener {
private RecyclerView recyclerView;
private TransactionAdapter adapter;
private List<Transaction> transactionList;
private List<Transaction> filteredList;
private ProgressBar progressBar;
private FloatingActionButton refreshButton;
private EditText searchEditText;
private ImageButton searchButton;
// Pagination variables
private int page = 0;
private final int limit = 10;
private boolean isLoading = false;
private boolean isLastPage = false;
private List<Transaction> transactionList = new ArrayList<>();
private String currentSearchQuery = "";
private boolean isRefreshing = false;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transaction);
// Set up the toolbar as the action bar
androidx.appcompat.widget.Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Initialize views
initViews();
// Enable the back button in the action bar
// Setup toolbar
setupToolbar();
// Setup RecyclerView
setupRecyclerView();
// Setup search functionality
setupSearch();
// Load initial data
loadTransactions(0);
}
private void initViews() {
recyclerView = findViewById(R.id.recyclerView);
progressBar = findViewById(R.id.progressBar);
searchEditText = findViewById(R.id.searchEditText);
searchButton = findViewById(R.id.searchButton);
}
private void setupToolbar() {
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
}
recyclerView = findViewById(R.id.recyclerView);
progressBar = findViewById(R.id.progressBar);
refreshButton = findViewById(R.id.refreshButton);
adapter = new TransactionAdapter(transactionList);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
private void setupRecyclerView() {
transactionList = new ArrayList<>();
filteredList = new ArrayList<>();
adapter = new TransactionAdapter(filteredList);
adapter.setPrintClickListener(this);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
refreshButton.setOnClickListener(v -> refreshTransactions());
// Add scroll listener for pagination
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (!recyclerView.canScrollVertically(1) && !isLoading && !isLastPage) {
// Pagination: Load more when reaching the bottom
if (!recyclerView.canScrollVertically(1) && !isLoading && !isLastPage && currentSearchQuery.isEmpty()) {
loadTransactions(page + 1);
}
// Detect scroll to top for refresh gesture (double tap on top)
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
if (layoutManager != null && layoutManager.findFirstCompletelyVisibleItemPosition() == 0 && dy < 0) {
// User is at top and scrolling up, enable refresh on search button long press
}
}
});
}
loadTransactions(0);
private void setupSearch() {
// Search button click listener
searchButton.setOnClickListener(v -> performSearch());
// Search button long press for refresh
searchButton.setOnLongClickListener(v -> {
refreshTransactions();
Toast.makeText(this, "Refreshing data...", Toast.LENGTH_SHORT).show();
return true;
});
// Search EditText listener
searchEditText.addTextChangedListener(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) {
currentSearchQuery = s.toString().trim();
filterTransactions(currentSearchQuery);
}
@Override
public void afterTextChanged(Editable s) {}
});
}
private void refreshTransactions() {
if (isRefreshing) return;
isRefreshing = true;
// Clear search when refreshing
searchEditText.setText("");
currentSearchQuery = "";
page = 0;
isLastPage = false;
transactionList.clear();
filteredList.clear();
adapter.notifyDataSetChanged();
loadTransactions(0);
}
private void performSearch() {
String query = searchEditText.getText().toString().trim();
currentSearchQuery = query;
filterTransactions(query);
// Hide keyboard
searchEditText.clearFocus();
}
private void filterTransactions(String query) {
filteredList.clear();
if (query.isEmpty()) {
filteredList.addAll(transactionList);
} else {
for (Transaction transaction : transactionList) {
if (transaction.referenceId.toLowerCase().contains(query.toLowerCase()) ||
transaction.amount.contains(query)) {
filteredList.add(transaction);
}
}
}
adapter.notifyDataSetChanged();
// Scroll to top after filtering
if (!filteredList.isEmpty()) {
recyclerView.scrollToPosition(0);
}
}
private void loadTransactions(int pageToLoad) {
isLoading = true;
progressBar.setVisibility(View.VISIBLE);
if (pageToLoad == 0) {
progressBar.setVisibility(View.VISIBLE);
}
new FetchTransactionsTask(pageToLoad).execute();
}
@ -149,33 +257,56 @@ public class TransactionActivity extends AppCompatActivity {
@Override
protected void onPostExecute(List<Transaction> transactions) {
isLoading = false;
isRefreshing = false;
progressBar.setVisibility(View.GONE);
if (error) {
Toast.makeText(TransactionActivity.this, "Failed to fetch transactions", Toast.LENGTH_SHORT).show();
return;
}
if (pageToLoad == 0) {
transactionList.clear();
}
transactionList.addAll(transactions);
adapter.notifyDataSetChanged();
// Update filtered list based on current search
filterTransactions(currentSearchQuery);
page = pageToLoad;
if (transactionList.size() >= total) {
isLastPage = true;
}
// Scroll to top if it's a refresh
if (pageToLoad == 0 && !filteredList.isEmpty()) {
recyclerView.scrollToPosition(0);
}
}
}
@Override
public boolean onOptionsItemSelected(android.view.MenuItem item) {
public void onPrintClick(Transaction transaction) {
// Handle print button click
Toast.makeText(this, "Cetak Ulang: " + transaction.referenceId, Toast.LENGTH_SHORT).show();
// Implement your print logic here
// For example, you might want to:
// 1. Generate receipt
// 2. Send to printer
// 3. Show print preview
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
// Handle the back button click
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
// Transaction model class
static class Transaction {
int id;
String type;
@ -201,4 +332,4 @@ public class TransactionActivity extends AppCompatActivity {
this.merchantName = merchantName;
}
}
}
}

View File

@ -4,6 +4,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
@ -14,11 +15,20 @@ import java.util.Locale;
public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.TransactionViewHolder> {
private List<TransactionActivity.Transaction> transactionList;
private OnPrintClickListener printClickListener;
public interface OnPrintClickListener {
void onPrintClick(TransactionActivity.Transaction transaction);
}
public TransactionAdapter(List<TransactionActivity.Transaction> transactionList) {
this.transactionList = transactionList;
}
public void setPrintClickListener(OnPrintClickListener listener) {
this.printClickListener = listener;
}
@NonNull
@Override
public TransactionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@ -30,19 +40,24 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
public void onBindViewHolder(@NonNull TransactionViewHolder holder, int position) {
TransactionActivity.Transaction t = transactionList.get(position);
// Set reference ID
holder.referenceId.setText(t.referenceId);
// Format the amount as Indonesian Rupiah
try {
double amountValue = Double.parseDouble(t.amount);
NumberFormat rupiahFormat = NumberFormat.getCurrencyInstance(new Locale.Builder().setLanguage("id").setRegion("ID").build());
holder.amount.setText(rupiahFormat.format(amountValue));
NumberFormat rupiahFormat = NumberFormat.getInstance(new Locale("id", "ID"));
holder.amount.setText("Rp. " + rupiahFormat.format(amountValue));
} catch (NumberFormatException e) {
holder.amount.setText("Rp " + t.amount);
holder.amount.setText("Rp. " + t.amount);
}
holder.status.setText(t.status);
holder.referenceId.setText(t.referenceId);
holder.merchantName.setText(t.merchantName);
holder.createdAt.setText(t.createdAt.replace("T", " ").substring(0, 19));
// Set click listener for print button
holder.printSection.setOnClickListener(v -> {
if (printClickListener != null) {
printClickListener.onPrintClick(t);
}
});
}
@Override
@ -51,14 +66,14 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
}
static class TransactionViewHolder extends RecyclerView.ViewHolder {
TextView amount, status, referenceId, merchantName, createdAt;
TextView amount, referenceId;
LinearLayout printSection;
public TransactionViewHolder(@NonNull View itemView) {
super(itemView);
amount = itemView.findViewById(R.id.textAmount);
status = itemView.findViewById(R.id.textStatus);
referenceId = itemView.findViewById(R.id.textReferenceId);
merchantName = itemView.findViewById(R.id.textMerchantName);
createdAt = itemView.findViewById(R.id.textCreatedAt);
printSection = itemView.findViewById(R.id.printSection);
}
}
}
}

View File

@ -0,0 +1,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#f5f5f5" />
<corners android:radius="4dp" />
<stroke android:width="1dp" android:color="#e0e0e0" />
</shape>

View File

@ -0,0 +1,4 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#666666" />
<corners android:radius="4dp" />
</shape>

View File

@ -12,10 +12,10 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:background="#F44336"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:title="Transactions History" />
app:title="CETAK ULANG STRUK" />
</com.google.android.material.appbar.AppBarLayout>
@ -23,9 +23,42 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:background="#f5f5f5"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<!-- Search Section -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp"
android:background="@android:color/white"
android:gravity="center_vertical">
<EditText
android:id="@+id/searchEditText"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:background="@drawable/search_background"
android:hint="Cari dengan nomor struk..."
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:textSize="14sp"
android:textColorHint="#999999" />
<ImageButton
android:id="@+id/searchButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:background="@drawable/search_button_background"
android:src="@android:drawable/ic_menu_search"
android:contentDescription="Search"
app:tint="@android:color/white" />
</LinearLayout>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
@ -38,17 +71,8 @@
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp" />
android:background="@android:color/white" />
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/refreshButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:contentDescription="Refresh"
android:src="@android:drawable/ic_popup_sync"
app:tint="@android:color/white" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,44 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:background="@android:color/white"
android:layout_marginBottom="8dp"
android:elevation="2dp">
android:orientation="vertical"
android:background="@android:color/white">
<TextView
android:id="@+id/textAmount"
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Amount"
android:textStyle="bold"
android:textSize="18sp" />
android:orientation="horizontal"
android:padding="16dp"
android:gravity="center_vertical">
<TextView
android:id="@+id/textStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Status"
android:textColor="#4CAF50"
android:textStyle="bold" />
<!-- Kolom 1: Reference ID -->
<TextView
android:id="@+id/textReferenceId"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="ref-eowu3pin"
android:textColor="#333333"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/textReferenceId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Reference ID" />
<!-- Kolom 2: Amount -->
<TextView
android:id="@+id/textAmount"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Rp. 1.111"
android:textColor="#666666"
android:textSize="14sp"
android:textAlignment="textEnd" />
<TextView
android:id="@+id/textMerchantName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Merchant Name" />
<!-- Kolom 3: Print Button -->
<LinearLayout
android:id="@+id/printSection"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical|end"
android:background="?android:attr/selectableItemBackground"
android:padding="8dp"
android:clickable="true"
android:focusable="true">
<TextView
android:id="@+id/textCreatedAt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Created At" />
</LinearLayout>
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_print"
android:layout_marginEnd="8dp"
app:tint="#666666" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cetak Ulang"
android:textColor="#666666"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
<!-- Bottom Border -->
<View
android:id="@+id/bottomBorder"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#e0e0e0" />
</LinearLayout>