效果圖
先上一個效果圖,看一下實現效果
大概說明一下:最外層佈局,是ViewPager,子項是Fragment,一屏幕顯示5個子項,中間位置突出,兩側子項縮放,並且隱藏在中間子項後邊,用陰影顯示
實現步驟
外層佈局
外層佈局就是一個ViewPager,先定義好:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".DemoActivity"
android:padding="16dp"
android:background="#ffffff"
android:clipChildren="false">
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#ffffff"
android:layout_margin="40dp"
/>
</LinearLayout>
兩點說明:1.android:clipChildren,是否限制子View在其範圍內,默認值爲true,也就是不允許進行擴展繪製,這裏設置爲false,還必須是在ViewPager的父佈局設置
2.android:layout_margin=“40dp”, 設置這個,是爲了給ViewPager兩側留出多餘位置,用來顯示兩側陰影
Fragment佈局
首先:Fragment裏,外層佈局是FrameLayout,裏邊有兩個佈局,一個是正常顯示,一個是用來改變透明度,實現陰影效果,這個以後會根據滑動位置,來切換顯示不同佈局,先定義兩個圓角矩形,一個是有陰影外層,作爲正常顯示佈局的背景,另一個是純色,作爲另一個佈局的背景
1.實現有陰影外層的圓角矩形
這個是用layer-list
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
>
<shape>
<corners android:radius="5dp"/>
<solid android:color="#F5F5F5"/>
</shape>
</item>
<item android:bottom="5dp"
android:right="5dp"
android:left="5dp"
android:top="5dp">
<shape>
<corners android:radius="5dp"/>
<solid android:color="#ffffff"/>
</shape>
</item>
</layer-list>
2.純色的圓角矩形
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- 填充顏色 -->
<solid android:color="#4184f5"/>
<!-- 線的寬度,顏色藍色 -->
<stroke android:width="1dp" android:color="#4184f5"/>
<!-- 矩形的圓角半徑 -->
<corners android:radius="5dp" />
</shape>
有了這兩個背景,剩下的就是佈局,也很簡單
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="300dp">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="@+id/ll_info"
android:layout_margin="5dp"
android:background="@drawable/rect_solid_and_stroke_white_corner_5dp"
>
<LinearLayout
android:id="@+id/ll_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center_vertical"
>
<ImageView
android:id="@+id/iv_image"
android:layout_width="180dp"
android:layout_height="180dp"
android:layout_gravity="center"
android:scaleType="fitXY"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/colorAccent"
android:layout_margin="10dp"
android:text="我是進度條"/>
<ProgressBar
android:id="@+id/new_people_progress"
android:layout_width="match_parent"
android:layout_height="12dp"
android:layout_margin="16dp"
android:max="100"
android:progress="40"
style="?android:attr/progressBarStyleHorizontal"
android:progressDrawable="@drawable/progressbar_color"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_zhegai"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/rect_solid_and_stroke_4184f5_corner_5dp"
android:orientation="horizontal"
android:visibility="gone"/>
</FrameLayout>
3.進度條效果:
佈局中,有一個進度條的效果,實現這樣的效果,也是需要使用layer-list,但是自定義的時候,希望進度條兩側都是圓角矩形,但是一般會clip成直角,這個解決方法,是自定義個shape去引用
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<item android:id="@android:id/background"
android:layout_width="wrap_content">
<shape>
<corners android:radius="10dp" />
<solid android:color="#f4f4f4" />
</shape>
</item>
<!-- 進度條 -->
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%"
android:drawable="@drawable/progress_bar_ct" />
</item>
</layer-list>
progress_bar_ct.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="#2583fd" />
<!-- padding設置內容區域離邊界的間距 -->
<!-- corners設置圓角,只適用於rectangle -->
<corners android:radius="10dp"/>
</shape>
</item>
</selector>
Fragment代碼
Fragment作爲演示,功能很簡單,根據定義,加載不同圖片
public class PictureFragment extends Fragment
{
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private int mParam1;
public PictureFragment()
{
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @return A new instance of fragment PictureFragment.
*/
// TODO: Rename and change types and number of parameters
public static PictureFragment newInstance(int param1)
{
PictureFragment fragment = new PictureFragment();
Bundle args = new Bundle();
args.putInt(ARG_PARAM1, param1);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (getArguments() != null)
{
mParam1 = getArguments().getInt(ARG_PARAM1);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_picture, container, false);
ImageView imageView = (ImageView) view.findViewById(R.id.iv_image);
imageView.setBackgroundResource(mParam1);
return view;
}
@Override
public void onAttach(Context context)
{
super.onAttach(context);
}
@Override
public void onDetach()
{
super.onDetach();
}
}
Activity代碼
{
.............
fragments.add(PictureFragment.newInstance(R.mipmap.p1));
fragments.add(PictureFragment.newInstance(R.mipmap.p2));
fragments.add(PictureFragment.newInstance(R.mipmap.p3));
fragments.add(PictureFragment.newInstance(R.mipmap.p4));
fragments.add(PictureFragment.newInstance(R.mipmap.p5));
fragments.add(PictureFragment.newInstance(R.mipmap.p6));
fragments.add(PictureFragment.newInstance(R.mipmap.p7));
CustomPagerAdapter customPagerAdapter = new CustomPagerAdapter(getSupportFragmentManager(), fragments);
viewPager.setPageTransformer(true, new CustomTransformer(DemoActivity.this));
viewPager.setAdapter(customPagerAdapter);
viewPager.setOffscreenPageLimit(5);
}
public class CustomPagerAdapter extends FragmentStatePagerAdapter
{
private List<Fragment> mFragments;
public CustomPagerAdapter(FragmentManager fm, List<Fragment> fragments)
{
super(fm);
this.mFragments = fragments;
fm.beginTransaction().commitAllowingStateLoss();
}
@Override
public Fragment getItem(int position)
{
return this.mFragments.get(position);
}
@Override
public int getCount()
{
return this.mFragments.size();
}
@Override
public int getItemPosition(@NonNull Object object)
{
return PagerAdapter.POSITION_NONE;
}
}
說明:1.Viewpager的setOffscreenPageLimit(int limit)方法,設置有多少的緩存Views
2.Viewpager的setPageTransformer(boolean reverseDrawingOrder, ViewPager.PageTransformer transformer)方法的第一個參數,用來控制加入到Viewpager的Views對象是正序的還是倒序的
特效實現
Viewpager實現特效,主要通過PageTransformer接口,不熟悉的可以先了解一下
Android ViewPager.PageTransformer詳解
Android——ViewPager實現3D畫廊效果
ViewPager實現層疊卡片
Android ViewPager使用方法小結
現在開始實現效果圖中的特效:
1.正常情況下,子項是不折疊到一起,通過設置 view.setTranslationX,按照子項寬度進行和位置進行平移,這樣才能摺疊到一起,pageWidth* position,爲了實現效果圖上左右兩側露出部分,可以通過改變偏移量實現,比如pageWidth * 0.9f * position之類
2.實現中間位置突出。設置摺疊後,正常情況下,後邊子項會遮蓋在前一子項上邊,這個通過設置setTranslationZ可以改變,這個translationZ設置view的層級,translationZ越大,說明他的層級越高,所以position = -1的view的層級就比position=0的view的層級低,那麼position=0的view就會疊在position=-1的view的上面,除中間位置以外,其他子項,都設置低一些,這樣才能突出中間子項
3.縮放可以通過view.setScaleX和 view.setScaleY來實現,設置不同的值,縮放不同的大小,通過這個,可以實現,最外層比次外層小的效果
4.除中間位置以外,其他位置,切換到純色背景,設置透明度,
public class CustomTransformer implements ViewPager.PageTransformer
{
private static final float MIN_SCALE = 0.85f;
private static final float MIN_ALPHA = 0.8f;
private static float defaultScale = 0.5f;
private Context context;
public CustomTransformer(Context context)
{
this.context = context;
}
@Override
public void transformPage(View view, float position)
{
int pageWidth = view.getWidth();
int pageHeight = view.getHeight();
float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
//一個頁面顯示5個子項,最左側的位置是-2,小於-2的,設置透明度爲0,隱藏
if (position < -2)
{
view.setAlpha(0);
view.setScaleX(defaultScale);
view.setScaleY(defaultScale);
}
else if (position >= -2 && position <= -1)
{ //中間位置左側的兩個子項
//設置透明度
view.setAlpha(0.1f);
// view.setAlpha((MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA))*0.5f);
if (position < -1 && position >= -2)//最左側子項,比前一子項縮放更小一些
{
view.setScaleX(scaleFactor * 0.9f);
view.setScaleY(scaleFactor * 0.9f);
}
else
{
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
}
/**
這個translationZ設置view的層級,translationZ越大,說明他的層級越高
所以position = -1的view的層級就比position=0的view的層級低,
那麼position=0的view就會疊在position=-1的view的上面,
除中間位置以外,其他子項,都設置低一些,這樣才能突出中間子項
*/
view.setTranslationZ(position);
//按照子項寬度進行和位置進行平移,這樣才能摺疊到一起
view.setTranslationX((-pageWidth * 0.9f * position));
//除中間位置顯示正常佈局以外,其他佈局,使用純色背景,並且設置透明度顯示
view.findViewById(R.id.ll_info).setVisibility(View.INVISIBLE);
view.findViewById(R.id.ll_zhegai).setVisibility(View.VISIBLE);
}
else if (position < 1)
{ // [-1,1]
// Modify the default slide transition to shrink the page as well
float vertMargin = pageHeight * (1 - scaleFactor) / 2;
float horzMargin = pageWidth * (1 - scaleFactor) / 2;
//中間位置透明度
if (position == 0)
{
view.setAlpha(1);
}
else
{
//中間位置滑動到兩側過程中,透明度變化
view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA));
}
view.findViewById(R.id.ll_info).setVisibility(View.VISIBLE);
view.findViewById(R.id.ll_zhegai).setVisibility(View.INVISIBLE);
if (position != 0)
{
view.setTranslationX((-pageWidth * 0.9f * position));
}
if (position > 0)
{
view.setTranslationZ(-position);
}
else
{
view.setTranslationZ(position);
}
if (position != 0)
{
//除中間位置不縮放以外,滑動到其他地方都縮放
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
}
// Fade the page relative to its size.
// view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA));
}
else if (position >= 1 && position <= 2)
{
view.setAlpha(0.1f);
// view.setAlpha((MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA))*0.5f);
if (position > 1 && position <= 2)
{
view.setScaleX(scaleFactor * 0.9f);
view.setScaleY(scaleFactor * 0.9f);
}
else
{
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
}
/**
這個translationZ設置view的層級,translationZ越大,說明他的層級越高
所以position = -1的view的層級就比position=0的view的層級低,那麼position=0的view就會疊在
position=-1的view的上面,除中間位置以外,其他子項,都設置低一些,這樣才能突出中間子項
*/
view.setTranslationZ(-position);
//按照子項寬度進行和位置進行平移,這樣才能摺疊到一起
view.setTranslationX((-pageWidth * 0.9f * position));
//除中間位置顯示正常佈局以外,其他佈局,使用純色背景,並且設置透明度顯示
view.findViewById(R.id.ll_info).setVisibility(View.INVISIBLE);
view.findViewById(R.id.ll_zhegai).setVisibility(View.VISIBLE);
}
else
{ //右側第二個子項以外,設置透明度爲0,不可見
view.setAlpha(0);
view.setScaleX(defaultScale);
view.setScaleY(defaultScale);
view.setTranslationZ(-position);
}
}
}