Baru-baru ini saya ditugaskan untuk membuat tab dengan indikator berwarna gradien. Saya telah mencoba perpustakaan pihak ketiga dan TabLayout Pustaka Dukungan untuk mendapatkan desain yang diinginkan, tetapi tidak berhasil. Pada satu titik saya mengganti metode onDraw untuk menggambar indikator saya sendiri menggunakan shader Paint dan LinearGradient, tetapi ternyata gradien ditarik ke latar belakang TabLayout alih-alih indikator.
Pada akhirnya, saya mendapatkan ide untuk membuat tampilan kustom dengan gradien yang dapat digambar dan menempatkannya di atas Tata Letak Tab dan menghitung terjemahan di sepanjang sumbu x untuk "menggesernya". Untuk artikel ini saya telah membuat proyek Github baru bernama GradientTabs dan tampilannya seperti ini:

- Untuk mencapai tampilan tab yang diinginkan, kita memerlukan yang berikut ini:
- TabLayout dengan tabIndicatorColor disetel ke null.
- Tampilan dengan latar belakang gradien, A ViewPager (kita akan menggunakan ViewPager.OnPageChangeListener untuk menghitung terjemahan), Dan FragmentPagerAdapter untuk ViewPager.
- Mari mulai coding!
Perpustakaan dibutuhkan
Tambahkan dua pustaka ini ke build.gradle aplikasi Anda:
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
Drawables
Yang pertama adalah latar belakang untuk indikator, gradient_bg.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape
android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="25dp"/>
<gradient
android:type="linear"
android:angle="45"
android:startColor="@color/colorPrimaryDark"
android:endColor="@color/colorPrimary"/>
</shape>
Mari beri TabLayout kami garis besar yang bagus dengan membuat drawable bernama tab_bg.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="25dp"/>
<stroke android:color="@color/colorPrimaryDark" android:width="1dp"/>
<solid android:color="#00000000"/>
</shape>
Dan akhirnya tata letak aktivitas kami, activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
tools:context=".MainActivity">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="32dp"
android:clipToPadding="false">
<View
android:id="@+id/indicator"
android:layout_width="0dp"
android:layout_height="50dp"
android:background="@drawable/gradient_bg"/>
<android.support.design.widget.TabLayout
android:id="@+id/tab"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/tab_bg"
app:tabGravity="fill"
app:tabMode="fixed"
app:tabSelectedTextColor="#ffffff"
app:tabIndicatorColor="@null"
app:tabRippleColor="@null"/>
</FrameLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
Perhatikan bahwa layout_width indikator diatur ke 0dp. Kami akan menetapkannya saat runtime dengan lebar yang diukur dari TabLayout dibagi dengan jumlah tab.
Logika Kode
Mari kita mulai menyatukan potongan-potongannya. Saya telah menulis kelas FragmentPagerAdapter dan Fragment sederhana untuk menunjukkan bahwa indikator khusus mengikuti gerakan gulir:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimaryDark">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textSize="30sp"
android:textColor="#ffffff"
android:text="Fragment One"/>
</RelativeLayout>
fragment_2.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textSize="30sp"
android:textColor="#ffffff"
android:text="Fragment Two"/>
</RelativeLayout>
FragmentOne.kt
package com.example.gradienttabs;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class FragmentOne extends Fragment {
public static FragmentOne newInstance() {
return new FragmentOne();
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_one, container, false);
return view;
}
}
FragmentTwo.kt
package com.example.gradienttabs;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class FragmentTwo extends Fragment {
public static FragmentTwo newInstance() {
return new FragmentTwo();
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_two, container, false);
return view;
}
}
TabFragmet.kt
package com.example.gradienttabs;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.util.ArrayList;
import java.util.List;
public class TabFragmentAdapter extends FragmentPagerAdapter {
private final List<Fragment> fragmentList = new ArrayList<>();
private final List<String> fragmentTitleList = new ArrayList<>();
public TabFragmentAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int i) {
return fragmentList.get(i);
}
@Override
public int getCount() {
return fragmentList.size();
}
@Override
public CharSequence getPageTitle(int position) {
return fragmentTitleList.get(position);
}
public void addFragment(Fragment fragment, String title) {
fragmentList.add(fragment);
fragmentTitleList.add(title);
}
}
Dan terakhir logika untuk menentukan terjemahan:
package com.example.gradienttabs;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.FrameLayout;
public class MainActivity extends AppCompatActivity {
TabLayout mTabs;
View mIndicator;
ViewPager mViewPager;
private int indicatorWidth;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Assign view reference
mTabs = findViewById(R.id.tab);
mIndicator = findViewById(R.id.indicator);
mViewPager = findViewById(R.id.viewPager);
//Set up the view pager and fragments
TabFragmentAdapter adapter = new TabFragmentAdapter(getSupportFragmentManager());
adapter.addFragment(FragmentOne.newInstance(), "Tab 1");
adapter.addFragment(FragmentTwo.newInstance(), "Tab 2");
mViewPager.setAdapter(adapter);
mTabs.setupWithViewPager(mViewPager);
//Determine indicator width at runtime
mTabs.post(new Runnable() {
@Override
public void run() {
indicatorWidth = mTabs.getWidth() / mTabs.getTabCount();
//Assign new width
FrameLayout.LayoutParams indicatorParams = (FrameLayout.LayoutParams) mIndicator.getLayoutParams();
indicatorParams.width = indicatorWidth;
mIndicator.setLayoutParams(indicatorParams);
}
});
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
//To move the indicator as the user scroll, we will need the scroll offset values
//positionOffset is a value from [0..1] which represents how far the page has been scrolled
//see https://developer.android.com/reference/android/support/v4/view/ViewPager.OnPageChangeListener
@Override
public void onPageScrolled(int i, float positionOffset, int positionOffsetPx) {
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams)mIndicator.getLayoutParams();
//Multiply positionOffset with indicatorWidth to get translation
float translationOffset = (positionOffset+i) * indicatorWidth ;
params.leftMargin = (int) translationOffset;
mIndicator.setLayoutParams(params);
}
@Override
public void onPageSelected(int i) {
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
}
}