Tutorial Membuat Tampilan Seperti Aplikasi OVO |
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 :
OVO Apps |
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_