Blog Tutorial Android Bagi Pemula

Sunday, June 28, 2020

Aplikasi Pengingat Tugas (To Do Reminder) Android

Selamat dini hari! Tutorial kali ini kita akan membuat sebuah aplikasi "Pengingat Tugas" atau dalam bahasa batak nya "To Do Reminder" atau "To Do List". Disini kita akan membutuhkan 5 java class termasuk MainActivity, 3 layout resource termasuk layout activity, dan 5 buah icon. Untuk penyimpanan kita akan menggunakan SQL Database. Sebelumnya blog ini belum pernah membahas secara khusus mengenai penyimpanan SQL Database, dan pada tutorial ini juga kita tidak akan membahasnya secara terperinci.


Baik, langkah pertama yang di lakukan tentunya membuat proyeknya terlebih dahulu. Proyek dalam tutorial ini diberikan nama com.gwnbs.proyek8. Supaya nantinya tidak kewalahan, kita buat dulu 5 buah icon melalui vector asset maker. Anda bisa melihat postingan khusus mengenai cara membuat vector icon pada halaman All Post ini. Berikut screenshoot yang memperlihatkan struktur dan penamaan folder dan file pada proyek ini, serta screenshoot aplikasinya saat di jalankan di emulator.



membuat aplikasi to do list di android
Gambar 1 : Menunjukan struktur dan penamaan folder dan file

Berikutnya kita ke activity_main.xml, disini kita hanya menambahkan 2 komponen yaitu sebuah Listview dan Button floating. Karena disini saya memakai floating action Button, maka diperlukan untuk menambahkan library material implementation 'com.google.android.material:material:1.1.0' ke build.gradle (Module : app) lalu lakukan sync gradle. Berikut isi dari activity_main.xml :

activity_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/itemsList"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="6dp"
        android:layout_marginBottom="50dp"
        android:divider="@null"
        android:dividerHeight="0dp"
        android:paddingLeft="6dp"
        android:paddingRight="6dp"/>
    
    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="15dp"
        android:backgroundTint="@android:color/holo_blue_dark"
        android:clickable="true"
        android:focusable="true"
        android:src="@drawable/ic_tambah"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        tools:ignore="UnusedAttribute" />
    
</RelativeLayout>

Selanjutnya kita perlu membuat 2 layout resource baru, yang mana kedua layout ini di fungsikan sebagai custom Listview dan custom AlertDialog. Untuk layout custom Listview diberikan nama daftar_todo.xml dan custom AlertDialog diberi nama custom_dialog_todo.xml. Berikut isi dari masing-masing file layout tersebut :

daftar_todo.xml :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:ignore="ContentDescription,
    UselessParent,UseCompoundDrawables,HardcodedText">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp">

        <ImageView
            android:id="@+id/delete"
            android:layout_width="36dp"
            android:layout_height="36dp"
            android:layout_alignParentEnd="true"
            android:layout_centerVertical="true"
            android:layout_marginEnd="16dp"
            android:background="@drawable/ic_hapus" />

        <LinearLayout
            android:id="@+id/titleRow"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            tools:ignore="UseCompoundDrawables">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:layout_marginEnd="16dp"
                android:layout_marginBottom="8dp"
                android:background="@drawable/ic_judul"
                android:tint="@android:color/holo_blue_dark" />

            <TextView
                android:id="@+id/title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:layout_marginBottom="8dp"
                android:text="Judul"
                android:textAllCaps="false"
                android:textSize="18sp" />
        </LinearLayout>

        <LinearLayout
            android:id="@+id/dateRow"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/titleRow"
            android:orientation="horizontal">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="16dp"
                android:layout_marginBottom="8dp"
                android:background="@drawable/ic_tanggal" />

            <TextView
                android:id="@+id/dateTitle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="4dp"
                android:layout_marginBottom="8dp"
                android:text="Tanggal"
                android:textAllCaps="true"
                android:textSize="12sp" />

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/dateRow"
            android:orientation="horizontal"
            tools:ignore="UseCompoundDrawables">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="16dp"
                android:layout_marginBottom="16dp"
                android:background="@drawable/ic_waktu"
                tools:ignore="ContentDescription" />

            <TextView
                android:id="@+id/timeTitle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="4dp"
                android:layout_marginBottom="16dp"
                android:text="Waktu"
                android:textAllCaps="true"
                android:textSize="12sp" />

        </LinearLayout>
    </RelativeLayout>
</RelativeLayout>

custom_dialog_todo.xml :
<RelativeLayout
    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:divider="@null"
    android:dividerPadding="0dp"
    android:orientation="vertical"
    android:padding="10dp"
    app:layout_constraintCircleRadius="8dp"
    tools:ignore="UseCompoundDrawables,
    ContentDescription,HardcodedText">

    <LinearLayout
        android:id="@+id/titleLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="16dp"
            android:src="@drawable/ic_judul" />

        <EditText
            android:id="@+id/edit_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:hint="Tugas"
            android:inputType="text"
            tools:ignore="Autofill" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/dateLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/titleLayout"
        android:orientation="horizontal">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="16dp"
            android:background="@drawable/ic_tanggal"/>

        <TextView
            android:id="@+id/date"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Tanggal"
            android:layout_marginTop="16dp"
            android:layout_marginBottom="16dp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/dateLayout"
        android:orientation="horizontal">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="16dp"
            android:background="@drawable/ic_waktu" />

        <TextView
            android:id="@+id/time"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Waktu"
            android:layout_marginTop="16dp"
            android:layout_marginBottom="16dp" />
    </LinearLayout>
</RelativeLayout>




Berikutnya membuat sebuah java class baru sebagai setter dan getter item-item nya, yaitu id, title, date dan time dari aplikasi "Pengingat Tugas" yang segera akan kita selesaikan ini. file ini di beri nama ModelData.java. Berikut isi lengkap dari file ini :

ModelData.java :
package com.gwnbs.proyek8;

public class ModelData {

    int id;
    private String title;
    private String date;
    private String time;

    ModelData(int id, String title, String date, String time) {
        this.id = id;
        this.title = title;
        this.date = date;
        this.time = time;
    }

    int getId() {
        return id;
    }

    String getTitle() {
        return title;
    }

    String getDate() {
        return date;
    }

    String getTime() {
        return time;
    }

}

Langkah berikutnya adalah membuat file java adaptor untuk SQL Database nya. Seperti terlihat pada gambar 1 di atas, file database ini di beri nama DatabaseHelper.java. Fungsinya tentu untuk Create, Read, Update dan Delete (CRUD) data.

DatabaseHelper.java :
package com.gwnbs.proyek8;

import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

import java.util.ArrayList;

public class DatabaseHelper extends SQLiteOpenHelper {

    private static final String TAG = "DatabaseHelper";

    private static final String TABLE_NAME = "ToDo_Table";
    private static final String COL1 = "ID";
    private static final String COL2 = "Name";
    private static final String COL3 = "Date";
    private static final String COL4 = "Time";

    public DatabaseHelper(Context context) {
        super(context, TABLE_NAME, null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String createTable = "CREATE TABLE " + TABLE_NAME + "("                + COL1 + " integer primary key, "                + COL2 + " TEXT, "                + COL3 + " DATE, "                + COL4 + " TIME" + ")";
        Log.d(TAG, "Creating table " + createTable);
        db.execSQL(createTable);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        onCreate(db);
    }

        //Memasukkan data ke database
        public boolean insertData(String item, String date, String time) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues contentValues = new ContentValues();
        contentValues.put(COL2, item);
        contentValues.put(COL3, date);
        contentValues.put(COL4, time);
        Log.d(TAG, "insertData: Inserting " + item + " to " + TABLE_NAME);
        long result = db.insert(TABLE_NAME, null, contentValues);
        db.close();
        return result != -1;
    }

        //Menghapus data dari database
        void deleteData(int id) {
        SQLiteDatabase db = this.getWritableDatabase();
        db.delete(TABLE_NAME, COL1 + "=" + id, null);
    }

        //Memuat semua data ke listview
        public ArrayList<ModelData> getAllData() {
        ArrayList<ModelData> arrayList = new ArrayList<>();
        SQLiteDatabase db = this.getReadableDatabase();
        String query = "SELECT * FROM " + TABLE_NAME;
        @SuppressLint("Recycle")
        Cursor cursor = db.rawQuery(query, null);

        while (cursor.moveToNext()) {
            int id = cursor.getInt(0);
            String title = cursor.getString(1);
            String date = cursor.getString(2);
            String time = cursor.getString(3);
            ModelData modelData = new ModelData(id, title, date, time);
            arrayList.add(modelData);
        }
        db.close();
        return arrayList;
    }
}

Jika ada kode yang di tandai atau di garis bawahi warna merah (error), abaikan dulu sementara sampai implementasi pembuatan aplikasi ini selesai. Tahap selanjutnya adalah membuat file java adaptor untuk listview. Implementasi penghapusan data dari database juga di lakukan di dalam adaptor ini. File ini diberikan nama ItemAdapter.java. Berikut isi keseluruhan dari file ini.

ItemAdapter.java :
package com.gwnbs.proyek8;

import android.annotation.SuppressLint;
import android.content.Context;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class ItemAdapter extends BaseAdapter {

    private Context context;
    private ArrayList<ModelData> arrayList;

    public ItemAdapter(Context context, ArrayList<ModelData> arrayList) {
        super();
        this.context = context;
        this.arrayList = arrayList;
    }

    @Override
    public int getCount() {
        return this.arrayList.size();
    }

    @Override
    public Object getItem(int position) {
        return arrayList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @SuppressLint({"ViewHolder", "InflateParams"})
    @Override
    public View getView(int position, View convertView, final ViewGroup parent) {
        LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        assert layoutInflater != null;
        convertView = layoutInflater.inflate(R.layout.daftar_todo, null);
        TextView titleTextView = convertView.findViewById(R.id.title);
        TextView dateTextView = convertView.findViewById(R.id.dateTitle);
        TextView timeTextView = convertView.findViewById(R.id.timeTitle);
        final ImageView delImageView = convertView.findViewById(R.id.delete);
        delImageView.setTag(position);

        //Menghapus tugas dari database saat icon hapus di klik
        delImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final int pos = (int) v.getTag();
                deleteItem(pos);
            }
        });

        ModelData modelData = arrayList.get(position);
        titleTextView.setText(modelData.getTitle());
        dateTextView.setText(modelData.getDate());
        timeTextView.setText(modelData.getTime());
        return convertView;
    }

        //Menghapus tugas dari listview
        private void deleteItem(int position) {
        deleteItemFromDb(arrayList.get(position).getId());
        arrayList.remove(position);
        notifyDataSetChanged();
    }

        //Menghapus tugas dari database
        private void deleteItemFromDb(int id) {
        DatabaseHelper databaseHelper = new DatabaseHelper(context);
        try {
            databaseHelper.deleteData(id);
            toastMsg("Tugas di hapus");
        } catch (Exception e) {
            e.printStackTrace();
            toastMsg("Oppss.. ada kesalahan saat menghapus");
        }
    }

        //Metode pesan toast
        private void toastMsg(String msg) {
        Toast t = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
        t.setGravity(Gravity.CENTER,0,0);
        t.show();
    }
}

Dan kelas java terakhir yang perlu dibuat adalah kelas Notifikasi.java. Kelas ini tentunya berfungsi untuk menghantarkan notifikasi ke smartphone pengguna setelah waktu pengingat di setel. Setelah kelas ini di buat, pada AndroidManifest.xml kita perlu melakukan pembaharuan, yaitu dengan menambahkan kelas ini sebagai receiver, juga kita perlu  menambahkan beberapa izin yang di perlukan. Berikut isi lengkap dari masing-masing file ini :

Notifikasi.java :
package com.gwnbs.proyek8;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class Notifikasi extends BroadcastReceiver {

    public static String NOTIFICATION_ID = "notification-id";
    public static String NOTIFICATION = "notification";

    public void onReceive(Context context, Intent intent) {
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        Notification notification = intent.getParcelableExtra(NOTIFICATION);
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel notificationChannel = new NotificationChannel(MainActivity.NOTIFICATION_CHANNEL_ID,
                    "Pengingat", importance);
            assert notificationManager != null;
            notificationManager.createNotificationChannel(notificationChannel);
        }
        int id = intent.getIntExtra(NOTIFICATION_ID, 0);
        if (notificationManager != null) {
            notificationManager.notify(id, notification);
        }
    }
}

AndroidManifest.xml :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.gwnbs.proyek8">

    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity"
            android:label="Daftar Tugas | gwnbs.com">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name=".Notifikasi" />
    </application>

</manifest>



Sekali lagi jika ada kode yang di warnai merah atau error, di abaikan dulu. Kecuali jika sudah selesai tutorial ini dan ada yang di tandai error barulah silahkan mencari dimana letak kesalahan nya.

Baik, tahap terakhir MainActivity.java. Cukup banyak koding yang di lakukan pada file ini, jika di jelaskan bisa panjang urusan nya. Pada setiap metode kodingan sudah saya buat sedikit-sedikit penjelasan nya. Jadi langsung saja berikut isi keseluruhan pada file activity ini :

MainActivity.java :
import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.DatePickerDialog;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.widget.AbsListView;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.Toast;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;

import com.google.android.material.floatingactionbutton.FloatingActionButton;

import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;

import static java.util.Calendar.MINUTE;

public class MainActivity extends AppCompatActivity {

    public static final String NOTIFICATION_CHANNEL_ID = "10001";
    private final static String default_notification_channel_id = "default";
    private static final String TAG = "MainActivity";
    private DatabaseHelper databaseHelper;
    private ListView itemsListView;
    private FloatingActionButton fab;
    private AlphaAnimation buttonClick = new AlphaAnimation(1F, 0.3F);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        databaseHelper = new DatabaseHelper(this);
        fab = findViewById(R.id.fab);
        itemsListView = findViewById(R.id.itemsList);

        populateListView();
        onFabClick();
        hideFab();
    }

    //Mengatur notifikasi
    private void scheduleNotification(Notification notification, long delay) {
        Intent notificationIntent = new Intent(this, Notifikasi.class);
        notificationIntent.putExtra(Notifikasi.NOTIFICATION_ID, 1);
        notificationIntent.putExtra(Notifikasi.NOTIFICATION, notification);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
                notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        AlarmManager alarmManager = (AlarmManager) getLayoutInflater().getContext().getSystemService(Context.ALARM_SERVICE);
        if (alarmManager != null) {
            alarmManager.set(AlarmManager.RTC_WAKEUP, delay, pendingIntent);
        }
    }

    private Notification getNotification(String content) {

        //Saat notifikasi di klik di arahkan ke MainActivity
        Intent intent = new Intent(this, MainActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(getLayoutInflater().getContext(), default_notification_channel_id);
        builder.setContentTitle("Pengingat");
        builder.setContentText(content);
        builder.setContentIntent(pendingIntent);
        builder.setAutoCancel(true);
        builder.setSmallIcon(R.drawable.ic_judul);
        builder.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_SOUND);
        builder.setChannelId(NOTIFICATION_CHANNEL_ID);
        builder.setPriority(NotificationCompat.PRIORITY_HIGH);
        return builder.build();
    }

    //Memasukkan data ke database
    private void insertDataToDb(String title, String date, String time) {
        boolean insertData = databaseHelper.insertData(title, date, time);
        if (insertData) {
            try {
                populateListView();
                toastMsg("Tugas di tambahkan");
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else
             toastMsg("Opps.. terjadi kesalahan saat menyimpan!");
    }

    //Mengambil seluruh data dari database ke listview
    private void populateListView() {
        try {
            ArrayList<ModelData> items = databaseHelper.getAllData();
            ItemAdapter itemsAdopter = new ItemAdapter(this, items);
            itemsListView.setAdapter(itemsAdopter);
            itemsAdopter.notifyDataSetChanged();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //Menyembunyikan tombol floating tambah saat listview di scroll
    private void hideFab() {
        itemsListView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                if (scrollState == SCROLL_STATE_IDLE) {
                    fab.show();
                }else{
                    fab.hide();
                }
            }
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            }
        });
    }

    private void onFabClick() {
        try {
            fab.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    v.startAnimation(buttonClick);
                    showAddDialog();
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //Implementasi klik dari tombol tambah
    @SuppressLint("SimpleDateFormat")
    private void showAddDialog() {
        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getLayoutInflater().getContext());
        LayoutInflater inflater = this.getLayoutInflater();
        @SuppressLint("InflateParams")
        final View dialogView = inflater.inflate(R.layout.custom_dialog_todo, null);
        dialogBuilder.setView(dialogView);

        final EditText judul = dialogView.findViewById(R.id.edit_title);
        final TextView tanggal = dialogView.findViewById(R.id.date);
        final TextView waktu = dialogView.findViewById(R.id.time);

        final long date = System.currentTimeMillis();
        SimpleDateFormat dateSdf = new SimpleDateFormat("d MMMM");
        String dateString = dateSdf.format(date);
        tanggal.setText(dateString);

        SimpleDateFormat timeSdf = new SimpleDateFormat("hh : mm a");
        String timeString = timeSdf.format(date);
        waktu.setText(timeString);

        final Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(System.currentTimeMillis());

        //Set tanggal
        tanggal.setOnClickListener(new View.OnClickListener() {
            @RequiresApi(api = Build.VERSION_CODES.N)
            @Override
            public void onClick(View v) {
                final DatePickerDialog datePickerDialog = new DatePickerDialog(getLayoutInflater().getContext(),
                        new DatePickerDialog.OnDateSetListener() {
                            @SuppressLint("SetTextI18n")
                            @Override
                            public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
                                String newMonth = getMonth(monthOfYear + 1);
                                tanggal.setText(dayOfMonth + " " + newMonth);
                                cal.set(Calendar.YEAR, year);
                                cal.set(Calendar.MONTH, monthOfYear);
                                cal.set(Calendar.DAY_OF_MONTH, dayOfMonth);
                            }
                        }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH));
                datePickerDialog.show();
                datePickerDialog.getDatePicker().setMinDate(date);
            }
        });

        //Set waktu
        waktu.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                TimePickerDialog timePickerDialog = new TimePickerDialog(getLayoutInflater().getContext(),
                        new TimePickerDialog.OnTimeSetListener() {
                            @Override
                            public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
                                String time;
                                @SuppressLint("DefaultLocale") String minTime = String.format("%02d", minute);
                                if (hourOfDay >= 0 && hourOfDay < 12) {
                                    time = hourOfDay + " : " + minTime + " AM";
                                } else {
                                    if (hourOfDay != 12) {
                                        hourOfDay = hourOfDay - 12;
                                    }
                                    time = hourOfDay + " : " + minTime + " PM";
                                }
                                waktu.setText(time);
                                cal.set(Calendar.HOUR, hourOfDay);
                                cal.set(Calendar.MINUTE, minute);
                                cal.set(Calendar.SECOND, 0);
                                Log.d(TAG, "onTimeSet: Time has been set successfully");
                            }
                        }, cal.get(Calendar.HOUR), cal.get(MINUTE), false);
                timePickerDialog.show();
            }
        });

        dialogBuilder.setTitle("Buat tugas baru");
        dialogBuilder.setPositiveButton("Tambah", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                String title = judul.getText().toString();
                String date = tanggal.getText().toString();
                String time = waktu.getText().toString();
                if (title.length() != 0) {
                    try {
                        insertDataToDb(title, date, time);
                        scheduleNotification(getNotification(title), cal.getTimeInMillis());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    toastMsg("Oops, Gak bisa kosong tugas nya.");
                }
            }
        });
        dialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                dialog.cancel();
            }
        });
        AlertDialog b = dialogBuilder.create();
        b.show();
    }

    //Metode pesan toast
    private void toastMsg(String msg) {
        Toast t = Toast.makeText(this, msg, Toast.LENGTH_SHORT);
        t.setGravity(Gravity.CENTER, 0,0);
        t.show();
    }

    //Mengkonversi bulan dari huruf menjadi angka
    private String getMonth(int month) {
        return new DateFormatSymbols().getMonths()[month - 1];
    }
}

Begitu saja, silahkan langsung di publish ke Google Play Store. Sekian dan terima kasih! Jika ada pertanyaan silahkan di pertanyakan di kolom komentar atau kontak form yang ada di bilah samping kiri.

UPDATE !

Berhubung adanya permintaan repositori github untuk tutorial ini, repositori nya pun sudah dibuat dan dapat di lihat di https://github.com/DaltrayNababan/pengingat.tugas
Share:

Tuesday, June 23, 2020

Mengganti Seluruh Font Teks Aplikasi Android Studio

Tutorial kali ini akan membahas cara mengganti font style teks yang ada pada seluruh aplikasi, termasuk font style judul pada ActionBar dengan menggunakan custom font.  Pada contoh disini akan menggunakan 2 aktivitas yaitu MainActivity dan SecondActivity guna memastikan bahwa font pada activity lain juga sama. Berikut tahapan-tahapan yang akan kita lakukan :
  1. Membuat proyek baru.
  2. Membuat activty baru.
  3. Membuat folder baru sebagai tempat penyimpanan font-font.
  4. Membuat 2 file java class baru.
  5. Memperbaharui AndroidManifest.xml
  6. Menambah style baru dan memperbaharui style tema pada style.xml
Baik kita mulai dengan membuat proyek baru, disini saya membuat nya dengan nama package com.gwnbs.proyek7. Pada layout xml activity_main dalam contoh tutorial ini hanya terdapat 2 komponen yakni sebuah TextView dan sebuah Button untuk menuju ke activity kedua. Lalu pada MainActivity.java juga hanya terdapat sebuah metode klik listener dari Button tersebut. Berikut isi dari kedua file tersebut :


activity_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_margin="10dp"
        android:textSize="20sp"
        android:text="@string/lorem_ipsum" />

    <Button
        android:id="@+id/main"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="30dp"
        android:text="Go to 2nd Activity"/>

</RelativeLayout>

MainActivity.java :
package com.gwnbs.proyek7;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button main = findViewById(R.id.main);
        main.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, SecondActivity.class));
            }
        });
    }
}

Ke tahap selanjutnya  yaitu membuat activity baru yakni SecondActivity.java dengan layout xml activity_second.xml. Pada kedua file ini juga kurang lebih sama isi di dalam nya dengan activty utama di atas, perbedaan nya hanya pada metode klik tombol, yang mana tentu saja tombol pada activity ini akan membawa kembali ke activity utama. Berikut isi dari kedua file activity second ini :

activity_second.xml :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SecondActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_margin="10dp"
        android:textSize="20sp"
        android:text="@string/lorem_ipsum" />

    <Button
        android:id="@+id/second"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="30dp"
        android:text="Go Back to Main"/>

</RelativeLayout>

SecondActivity.java :
package com.gwnbs.proyek7;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        Button second = findViewById(R.id.second);
        second.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(SecondActivity.this, MainActivity.class));
            }
        });
    }
}

Tahap ketiga membuat direktori baru untuk penyimpanan font-font custom, yaitu direktori "fonts" yang harus berada di dalam folder "assets". Sehingga terlebih dahulu kita harus membuat folder asset nya. Untuk membuatnya, klik kanan pada folder res > New > Folder > Assets Folder, akan muncul tab konfigurasi dan langsung saja klik Finish. Folder "assets" akan muncul sejajar tepat di atas folder res.

Selanjutnya membuat direktori "fonts". Klik kanan pada folder "assets" yang baru di buat tadi New > Directory, pada tab konfigurasi isikan dengan nama fonts kemudian langsung klik Enter. Disini saya sudah menggunakan Android Studio 4.0, cukup banyak perubahan dari versi sebelumnya, termasuk tab-tab konfigurasi saat membuat file-file baru juga mengalami perubahan. Berikut gambar-gambar panduan dalam membuat folder dan direktori nya :

Cara Buat Asset Folder android Studio
Gambar 1 : Panduan membuat assets folder

How to Set Custom Font Android Studio
Gambar 2 : Panduan membuat direktori fonts

Berikutnya memasukkan font yang ingin di gunakan ke dalam folder tersebut. Ada beberapa cara dalam mendownload font. Pertama Anda bisa mendownload nya langsung melalui Android Studio dengan cara : Arahkan dan parkirkan cursor Anda pada sebuah komponen TextView yang ada pada layout xml, kemudian klik tab "design", akan muncul kolom atribut-atribut; scroll terus ke bawah sampai ketemu dengan atribut "fontFamily". Kemudian klik icon dropdown pada atribut tersebut dan pilih "more fonts". Berikut di bawah gambar panduannya, sekaligus menunjukkan struktur folder keseluruhan pada proyek contoh ini :

Download font Family dari Android Studio
Gambar 3 : Cara download font dari Android Studio

Seteleah mendownload font dengan cara di atas, secara otomatis di dalam folder res akan muncul folder baru yaitu "font". Copy atau cut file font yang ada pada folder font tersebut, kemudian copy ke dalam direktori "fonts" yang ada di dalam folder "assets". Namun, pastikan terlebih dahulu file font nya ber format .ttf atau .otf, karena jika lain dari itu tidak akan bisa.

Kekurangan mendownload dari cara pertama ini adalah, tidak ada opsi yang menunjukkan format font nya apa sebelum mendownloadnya. Yang saya alami lebih sering mendapatkan font dengan format "xml", dan setelah mencobanya, format ini tidak support untuk mengatur font style seluruh teks pada aplikasi.

Cara mendownload berikut nya yang lebih di rekomendasikan adalah dengan langsung mengunjungi website Google Font. Selama ini saya mendownload font dari website nya langsung selalu mendapatkan format .ttf. Website alternatif lain yang bisa Anda kunjungi untuk mendownload font yaitu 1001fontSetelah font di download, ekstrak terlebih dahulu cukup hanya file .ttf atau .otf nya saja, kemudian copy dan pastekan ke dalam direktori "fonts" pada folder "assets". Seperti yang terlihat pada gambar 3 di atas.

Langkah selanjutnya membuat 2 java class baru. Seperti yang terlihat pada gambar 3 di atas, file java dalam contoh ini saya buat dengan nama TypeFace.java dan FonPath.java. File pertama berfungsi untuk pengaturan custom font yang ingin di gunakan, dan kelas FontPath di fungsikan untuk menyamakan font pada seluruh activity dengan memperluas nya Apllication; ini bila seluruh activity memperluas base class yang sama, pada contoh ini AppCompatActivity. Berikut isi dari kedua kelas java ini :

TypeFace.java :
package com.gwnbs.proyek7;

import android.content.Context;
import android.graphics.Typeface;

import java.lang.reflect.Field;

public class TypeFace {

    public static void overrideFont(Context context, String defaultFontNameToOverride, String customFontFileNameInAssets) {
        try {
            final Typeface customFontTypeface = Typeface.createFromAsset(context.getAssets(), customFontFileNameInAssets);

            final Field defaultFontTypefaceField = Typeface.class.getDeclaredField(defaultFontNameToOverride);
            defaultFontTypefaceField.setAccessible(true);
            defaultFontTypefaceField.set(null, customFontTypeface);
        } catch (Exception ignored) {
        }
    }
}

FontPath.java :
package com.gwnbs.proyek7;

import android.app.Application;

public class FontPath extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        TypeFace.overrideFont(getApplicationContext(), "SERIF","fonts/NiconneRegular.ttf");
    }
}

Pada kelas FontPath.java di atas, di dalam metode onCreate di masukkan sumber folder dan nama file font yang ingin di pakai. Jika ingin mengganti dengan font lainnya, tinggal ganti saja nama file font nya dengan nama file font yang di inginkan. Ok, berikutnya adalah memperbaharui AndroidManifest.xml, yaitu dengan menambahkan atribut android:name = ".FontPath" di dalam tag <application> </application>.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.gwnbs.proyek7">

    <application
        android:name=".FontPath"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".SecondActivity"
            android:label="Second Activity | gwnbs.com"/>

        <activity android:name=".MainActivity"
            android:label="Font Style | gwnbs.com">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Terakhir pada style.xml, pertama-tama menambahkan beberapa style baru supaya font style pada title di ActionBar juga mengikuti font style yang di buat, kemudian memasukkan nya sebagai item ke style theme yang di gunakan dan menambahkan item typeface ke dalam nya. Berikut isi dari style.xml :

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">#E53935</item>
        <item name="colorPrimaryDark">#111112</item>
        <item name="colorAccent">#F4511E</item>
        <item name="android:typeface">serif</item>
        <item name="actionBarStyle">@style/Theme.AppLib.ActionBar.Solid</item>
    </style>

    <style name="Theme.AppLib.ActionBar.Solid" parent="@style/Widget.AppCompat.Light.ActionBar.Solid">
        <item name="titleTextStyle">@style/Theme.AppLib.ActionBar.TitleTextStyle</item>
    </style>

    <style name="Theme.AppLib.ActionBar.TitleTextStyle" parent="">
        <item name="android:textSize">30sp</item>
    </style>

</resources>

Cara mengganti font di Android Studio
Gambar 4 : Activity utama

Menggunakan custom font di android studio
Gambar 5 : Activity second

Terlihat pada gambar-gambar di atas, seluruh font teks berubah, begitu pun font teks yang ada pada tombol. Berakhir sudah tutorial ini, apabila ada yang ingin di sampaikan silahkan di post di komentar atau contact form yang ada pada bilah samping kiri.
Share:

Hubungi Saya

Name

Email *

Message *

Terlaris 30 Hari Terakhir