Sunday, August 25, 2019

Tutorial Membuat Tampilan Seperti Aplikasi OVO dengan Android Studio

Tutorial Membuat Tampilan Seperti Aplikasi OVO dengan Android Studio
Tutorial Membuat Tampilan Seperti Aplikasi OVO
السَّلاَمُ عَلَيْكُمْ وَرَحْمَةُ اللهِ وَبَرَكَاتُه sobat 48😁. Bagaimana kabarnya? Sudah beberapa minggu saya tidak update Blog Rivaldi 48 ini ya. Semua itu dikarenakan saya yang sibuk bekerja. Tetapi tenang saja, sebisa mungkin saya akan terus membagikan artikel tutorial-tutorial yang bermanfaat buat sobat 48.

Kali ini saya akan membagikan sebuah artikel Tutorial Membuat Tampilan Seperti Aplikasi OVO dengan Android Studio. Loh kenapa aplikasi OVO? Kenapa tidak aplikasi yang lain? Jadi gini, pada aplikasi OVO, list untuk menampilkan daftar menunya itu berbeda dari aplikasi lainnya. Saya pun tertarik untuk mencoba menirunya. Untuk tampilan di aplikasi OVO seperti ini :
Tutorial Membuat Tampilan Seperti Aplikasi OVO dengan Android Studio
OVO Apps
Keren kan ada Diagonal ImageView nya? Itu yang membuat saya terinspirasi untuk membuatkan tutorial ini. Jika kalian ingin SOURCE CODE sample aplikasi ini, silahkan download DISINI. Tetapi jika kalian ingin tahu cara mengaplikasikannya, silahkan lanjut baca artikel ini sampai selesai. Untuk kalian yang ingin mencoba membuat aplikasi ini dengan tutorial versi video, berikut saya berikan Videonya:

Jangan lupa subscribe Channel Youtube saya juga ya Azhar Rivaldi, karena disana ada banyak tutorial-tutorial untuk membuat aplikasi lainnya. Oke langsung saja tanpa basa-basi lagi kita langsung ke langkah pertama :

1. Buat project baru di Android Studio dengan cara klik File ⇒ Project Baru. Ketika diminta untuk memilih Default Activity, pilih Empty Activity dan klik next. Untuk minSDK, disini saya set API 21 ya.

2. Tambahkan beberapa library di build.gradle :


dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'com.google.android.material:material:1.0.0'
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation 'androidx.recyclerview:recyclerview:1.0.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    implementation 'de.hdodenhof:circleimageview:2.2.0'
}

3. Buat MainActivity.java, activity_main.xml dan list_item_data untuk tampilan menu utamanya :


package com.azhar.diagonalrecyclerview.activities;

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.azhar.diagonalrecyclerview.R;
import com.azhar.diagonalrecyclerview.adapter.ProductAdapter;
import com.azhar.diagonalrecyclerview.items.Product;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Azhar Rivaldi on 18/08/2019.
 */

public class MainActivity extends AppCompatActivity {

    List productList;

    //the recyclerview
    RecyclerView recyclerView;

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

        //getting the recyclerview from xml
        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        //initializing the productlist
        productList = new ArrayList<>();


        //adding some items to our list
        productList.add(
                new Product(
                        1,
                        "Smartphone Realme 3 Pro",
                        "6.3 inch, 6/128, Android 9.0 (Pie) ColorOS 6",
                        9.2,
                        "3.400.000",
                        R.drawable.realme_3_pro, R.drawable.logo_realme));

        productList.add(
                new Product(
                        1,
                        "Smartphone Redmi Note 7",
                        "6.3 inch, 4/64, Android 9 Pie, MIUI 10",
                        9.5,
                        "2.700.000",
                        R.drawable.redmi_note_7, R.drawable.logo_mi));

        productList.add(
                new Product(
                        1,
                        "Smartphone Samsung A50",
                        "6.4 inch, 6/128, Android 9 Pie",
                        8.5,
                        "4.100.000",
                        R.drawable.a50, R.drawable.samsung));

        productList.add(
                new Product(
                        1,
                        "Smartphone Realme X",
                        "6.53 inch, 6/128, Android 9.0 (Pie), ColorOS 6",
                        9.5,
                        "3.700.000",
                        R.drawable.realme_x, R.drawable.logo_realme));

        productList.add(
                new Product(
                        1,
                        "Smartphone Oppo F11",
                        "6.53 inch, 4/128, Android 9.0 (Pie), ColorOS 6",
                        8.9,
                        "3.700.000",
                        R.drawable.oppo_f11, R.drawable.logo_oppo));

        productList.add(
                new Product(
                        1,
                        "Smartphone VIVO Z1 Lite",
                        "6.26 inch, 4/32, Android 8.0 (Oreo)",
                        8.9,
                        "3.100.000",
                        R.drawable.vivo_z1, R.drawable.logo_vivo));

        productList.add(
                new Product(
                        1,
                        "Smartphone ASUS Max Pro M2",
                        "6.3 inch, 4/64, Android 9.0 (Pie)",
                        8.6,
                        "3.100.000",
                        R.drawable.max_pro_m2, R.drawable.logo_asus));

        //creating recyclerview adapter
        ProductAdapter adapter = new ProductAdapter(this, productList);

        //setting adapter to recyclerview
        recyclerView.setAdapter(adapter);
    }
}



<?xml version="1.0" encoding="utf-8"?>
<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"
    tools:context=".activities.MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>



<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.google.android.material.card.MaterialCardView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:cardMaxElevation="4dp"
            app:cardCornerRadius="5dp"
            android:layout_margin="5dp">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

                <com.azhar.diagonalrecyclerview.model.DiagonalImageView
                    android:id="@+id/imageIcon"
                    android:src="@drawable/logo_realme"
                    android:layout_width="150dp"
                    android:layout_height="120dp"
                    android:scaleType="centerCrop"
                    app:di_distance="56dp"
                    app:di_end="top"
                    app:di_start="right" />

                <RelativeLayout
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_toEndOf="@id/imageIcon"
                    android:padding="3dp">

                    <TextView
                        android:id="@+id/textViewTitle"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginStart="10dp"
                        android:textAppearance="@style/Base.TextAppearance.AppCompat.Small"
                        android:textColor="#000000" />

                    <TextView
                        android:id="@+id/textViewShortDesc"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_below="@id/textViewTitle"
                        android:layout_marginStart="10dp"
                        android:layout_marginTop="5dp"
                        android:textAppearance="@style/Base.TextAppearance.AppCompat.Small" />

                    <TextView
                        android:id="@+id/textViewRating"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_below="@id/textViewShortDesc"
                        android:layout_marginStart="10dp"
                        android:layout_marginTop="5dp"
                        android:background="@color/colorAccent"
                        android:paddingLeft="15dp"
                        android:paddingRight="15dp"
                        android:textAppearance="@style/Base.TextAppearance.AppCompat.Small.Inverse"
                        android:textStyle="bold" />

                    <TextView
                        android:id="@+id/textViewPrice"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_below="@id/textViewRating"
                        android:layout_marginStart="10dp"
                        android:layout_marginTop="5dp"
                        android:textAppearance="@style/Base.TextAppearance.AppCompat.Large"
                        android:textStyle="bold" />

                </RelativeLayout>

            </RelativeLayout>

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

    </LinearLayout>

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/imageList"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:src="@drawable/realme_3_pro"
        android:layout_gravity="center|left"
        android:layout_marginLeft="80dp"
        android:elevation="4dp"
        app:civ_border_color="@color/colorAccent"
        app:civ_border_width="2dp"
        tools:ignore="RtlHardcoded" />

</FrameLayout>

4. Buat ProductAdapter.java :


package com.azhar.diagonalrecyclerview.adapter;

import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.azhar.diagonalrecyclerview.R;
import com.azhar.diagonalrecyclerview.items.Product;
import com.azhar.diagonalrecyclerview.model.DiagonalImageView;

import java.util.List;

import de.hdodenhof.circleimageview.CircleImageView;

/**
 * Created by Azhar Rivaldi on 18/08/2019.
 */

public class ProductAdapter extends RecyclerView.Adapter {


    //this context we will use to inflate the layout
    private Context mCtx;

    //we are storing all the products in a list
    private List productList;

    //getting the context and product list with constructor
    public ProductAdapter(Context mCtx, List productList) {
        this.mCtx = mCtx;
        this.productList = productList;
    }

    @NonNull
    @Override
    public ProductViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        //inflating and returning our view holder
        LayoutInflater inflater = LayoutInflater.from(mCtx);
        @SuppressLint("InflateParams") View view = inflater.inflate(R.layout.list_item_data, null);
        return new ProductViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ProductViewHolder holder, int position) {
        //getting the product of the specified position
        Product product = productList.get(position);

        //binding the data with the viewholder views
        holder.textViewTitle.setText(product.getTitle());
        holder.textViewShortDesc.setText(product.getShortdesc());
        holder.textViewRating.setText(String.valueOf(product.getRating()));
        holder.textViewPrice.setText(String.valueOf(product.getPrice()));

        holder.imageIcon.setImageDrawable(mCtx.getResources().getDrawable(product.getImageIcon()));
        holder.imageList.setImageDrawable(mCtx.getResources().getDrawable(product.getImageList()));

    }

    @Override
    public int getItemCount() {
        return productList.size();
    }

    class ProductViewHolder extends RecyclerView.ViewHolder {

        TextView textViewTitle, textViewShortDesc, textViewRating, textViewPrice;
        CircleImageView imageList;
        DiagonalImageView imageIcon;

        @SuppressLint("CutPasteId")
        public ProductViewHolder(View itemView) {
            super(itemView);

            textViewTitle = itemView.findViewById(R.id.textViewTitle);
            textViewShortDesc = itemView.findViewById(R.id.textViewShortDesc);
            textViewRating = itemView.findViewById(R.id.textViewRating);
            textViewPrice = itemView.findViewById(R.id.textViewPrice);
            imageIcon = itemView.findViewById(R.id.imageIcon);
            imageList = itemView.findViewById(R.id.imageList);
        }
    }

}


5. Buat Product.java :


package com.azhar.diagonalrecyclerview.items;

/**
 * Created by Azhar Rivaldi on 18/08/2019.
 */

public class Product {

    private int id;
    private String title;
    private String shortdesc;
    private double rating;
    private String price;
    private int imageList, imageIcon;

    public Product(int id, String title, String shortdesc, double rating, String price, int imageList, int imageIcon) {
        this.id = id;
        this.title = title;
        this.shortdesc = shortdesc;
        this.rating = rating;
        this.price = price;
        this.imageList = imageList;
        this.imageIcon = imageIcon;
    }

    public int getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public String getShortdesc() {
        return shortdesc;
    }

    public double getRating() {
        return rating;
    }

    public String getPrice() {
        return price;
    }

    public int getImageList() {
        return imageList;
    }

    public int getImageIcon() {
        return imageIcon;
    }

}


6. Buat DiagonalImageView.kt untuk membuat tampilan ImageView berupa Diagonal :


package com.azhar.diagonalrecyclerview.model

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.graphics.Paint.Style
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.appcompat.widget.AppCompatImageView
import com.azhar.diagonalrecyclerview.R

/**
 * Created by Azhar Rivaldi on 18/08/2019.
 */

class DiagonalImageView : AppCompatImageView {

    private val clipPath by lazy { Path() }
    private val borderPath by lazy { Path() }
    private val borderPaint by lazy { Paint(Paint.ANTI_ALIAS_FLAG) }
    private val clickRegion by lazy { Region() }
    private val clickRect by lazy { RectF() }

    var start = NONE
    var end = NONE
    var distance = 0f

    var borderEnabled = false
    var borderSize = 0f
    var borderColor = Color.BLACK

    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        init(context, attrs)
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
            super(context, attrs, defStyleAttr) {
        init(context, attrs)
    }

    private fun init(context: Context, attrs: AttributeSet?) {
        attrs?.let {
            val a = context.obtainStyledAttributes(it, R.styleable.DiagonalImageView)

            with(a) {
                start = getInt(R.styleable.DiagonalImageView_di_start, NONE)
                end = getInt(R.styleable.DiagonalImageView_di_end, NONE)
                distance = getDimensionPixelSize(R.styleable.DiagonalImageView_di_distance, 0).toFloat()
                borderEnabled = getBoolean(R.styleable.DiagonalImageView_di_borderEnabled, false)
                borderSize = getDimensionPixelSize(R.styleable.DiagonalImageView_di_borderSize, 0).toFloat()
                borderColor = getColor(R.styleable.DiagonalImageView_di_borderColor, Color.BLACK)
                recycle()
            }

            borderPaint.apply {
                style = Style.STROKE
                color = borderColor
                strokeWidth = borderSize
            }

            // refer this https://developer.android.com/guide/topics/graphics/hardware-accel.html#unsupported
            val layerType =
                    if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) LAYER_TYPE_HARDWARE else LAYER_TYPE_SOFTWARE
            setLayerType(layerType, null)
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        event
                ?.takeUnless { clickRegion.isEmpty }
                ?.actionMasked
                ?.takeIf {
                    it == MotionEvent.ACTION_DOWN &&
                            !clickRegion.contains(event.x.toInt(), event.y.toInt())
                }
                ?.run {
                    return false
                }
        return super.onTouchEvent(event)
    }

    override fun dispatchDraw(canvas: Canvas?) {
        canvas
                ?.takeUnless { clipPath.isEmpty }
                ?.run {
                    clipPath(clipPath)
                }
        super.dispatchDraw(canvas)
    }

    override fun onDraw(canvas: Canvas?) {
        if (clipPath.isEmpty) {
            super.onDraw(canvas)
            return
        }
        canvas?.apply {
            val lastSave = save()
            clipPath(clipPath)
            super.onDraw(this)
            // draw border
            borderPath.takeUnless { it.isEmpty }
                    ?.run {
                        drawPath(this, borderPaint)
                    }
            restoreToCount(lastSave)
        }
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        if (changed) {
            setClipPath()
        }
    }

    override fun invalidate() {
        super.invalidate()
        setClipPath()
    }

    private fun setClipPath() {
        val width = measuredWidth.toFloat()
        val height = measuredHeight.toFloat()

        if (width <= 0 || height <= 0) {
            return
        }

        clipPath.reset()
        borderPath.reset()

        when (start) {
            TOP -> {
                if (end == TOP || end == LEFT) {
                    clipPath.apply {
                        moveTo(0f, 0f)
                        lineTo(width, distance)
                        lineTo(width, height)
                        lineTo(0f, height)
                    }

                    borderPath.takeIf { borderEnabled }
                            ?.apply {
                                moveTo(0f, 0f)
                                lineTo(width, distance)
                            }
                } else {
                    clipPath.apply {
                        moveTo(0f, distance)
                        lineTo(width, 0f)
                        lineTo(width, height)
                        lineTo(0f, height)
                    }

                    borderPath.takeIf { borderEnabled }
                            ?.apply {
                                moveTo(0f, distance)
                                lineTo(width, 0f)
                            }
                }
            }
            BOTTOM -> {
                if (end == TOP || end == LEFT) {
                    clipPath.apply {
                        moveTo(0f, 0f)
                        lineTo(width, 0f)
                        lineTo(width, height - distance)
                        lineTo(0f, height)
                    }

                    borderPath.takeIf { borderEnabled }
                            ?.apply {
                                moveTo(0f, height)
                                lineTo(width, height - distance)
                            }
                } else {
                    clipPath.apply {
                        moveTo(0f, 0f)
                        lineTo(width, 0f)
                        lineTo(width, height)
                        lineTo(0f, height - distance)
                    }

                    borderPath.takeIf { borderEnabled }
                            ?.apply {
                                moveTo(0f, height - distance)
                                lineTo(width, height)
                            }
                }
            }
            LEFT -> {
                if (end == TOP || end == LEFT) {
                    clipPath.apply {
                        moveTo(distance, 0f)
                        lineTo(width, 0f)
                        lineTo(width, height)
                        lineTo(0f, height)
                    }

                    borderPath.takeIf { borderEnabled }
                            ?.apply {
                                moveTo(distance, 0f)
                                lineTo(0f, height)
                            }
                } else {
                    clipPath.apply {
                        moveTo(0f, 0f)
                        lineTo(width, 0f)
                        lineTo(width, height)
                        lineTo(distance, height)
                    }

                    borderPath.takeIf { borderEnabled }
                            ?.apply {
                                moveTo(0f, 0f)
                                lineTo(distance, height)
                            }
                }
            }
            RIGHT -> {
                if (end == TOP || end == LEFT) {
                    clipPath.apply {
                        moveTo(0f, 0f)
                        lineTo(width, 0f)
                        lineTo(width - distance, height)
                        lineTo(0f, height)
                    }

                    borderPath.takeIf { borderEnabled }
                            ?.apply {
                                moveTo(width, 0f)
                                lineTo(width - distance, height)
                            }
                } else {
                    clipPath.apply {
                        moveTo(0f, 0f)
                        lineTo(width - distance, 0f)
                        lineTo(width, height)
                        lineTo(0f, height)
                    }

                    borderPath.takeIf { borderEnabled }
                            ?.apply {
                                moveTo(width - distance, 0f)
                                lineTo(width, height)
                            }
                }
            }
            else -> return
        }

        clipPath.close()
        borderPath.close()

        clipPath.computeBounds(clickRect, true)
        val region = Region(
                clickRect.left.toInt(),
                clickRect.top.toInt(),
                clickRect.right.toInt(),
                clickRect.bottom.toInt()
        )
        clickRegion.setPath(clipPath, region)
    }

    companion object {
        const val NONE = 0
        const val LEFT = 1
        const val TOP = 2
        const val RIGHT = 3
        const val BOTTOM = 4
    }
}



7. Yang terakhir tambahkan values attrs.xml :


<?xml version="1.0" encoding="utf-8"?>
<resources>

  <declare-styleable name="DiagonalImageView">
    <attr format="enum" name="di_start">
      <enum name="left" value="1"/>
      <enum name="top" value="2"/>
      <enum name="right" value="3"/>
      <enum name="bottom" value="4"/>
    </attr>
    <attr format="enum" name="di_end">
      <enum name="left" value="1"/>
      <enum name="top" value="2"/>
      <enum name="right" value="3"/>
      <enum name="bottom" value="4"/>
    </attr>
    <attr format="dimension" name="di_distance"/>
    <attr format="dimension" name="di_borderSize"/>
    <attr format="color" name="di_borderColor"/>
    <attr format="boolean" name="di_borderEnabled"/>
  </declare-styleable>

</resources>

8. Selesai dan kalian Run. Jika kalian mengikuti langkah-langkah diatas dengan baik, pasti aplikasi yang kalian buat akan berjalan sebagaimana mestinya. Namun jika mengalami Error, silahkan berikan komentar dan kita diskusikan bersama.

Demikian informasi yang saya bagikan untuk kalian. Jangan lupa bagikan artikel ini ke teman-teman kalian agar ikut membaca Tutorial Membuat Tampilan Seperti Aplikasi OVO dengan Android Studio ini. Subscribe juga blog Rivaldi 48 ini agar kalian mendapatkan notifikasi saat Admin update artikel terbaru. Semoga kalian lebih nyaman dan mudah dalam mengakses Blog Rivaldi 48 dimanapun kalian berada. Terima Kasih. Follow Instagram Admin @azhardvls_

5 comments

  1. blog kakak sangat bermanfaat bagi saya terimkasih kak klok bisa boleh nanyak nanyak tutorial kak

    ReplyDelete
  2. Keren kak..
    Bagi Emailnya dong.. Sharing-Sharing :)






    #Linkaja88

    ReplyDelete
    Replies
    1. jika ada kepentingan atau butuh jasa, silhakan hubungi saya melalui contact us. terima kasih

      Delete

Silahkan tinggalkan komentar jika Anda punya saran, kritik, atau pertanyaan seputar topik pembahasan. Gunakan bahasa yang bijak dan santun. Terima Kasih.
EmoticonEmoticon