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_