Mục đích của bài viết là hướng dẫn cách tạo 1 slide image trong item của recycler view
1. Model, data, view, interface
1.1. Tạo model Profile
Tạo 1 model chứa thông tin Profile : gồm id, name, age, và list image
package tuananh.com.slideimageinrecyclerview.model;
import java.io.Serializable;
import java.util.List;
/**
* Created by framgia on 27/04/2017.
*/
public class Profile implements Serializable {
private int mId;
private String mName;
private int mAge;
private List<String> mImageList;
public Profile(int id, String name, int age, List<String> imageList) {
mId = id;
mName = name;
mAge = age;
mImageList = imageList;
}
public int getId() {
return mId;
}
public void setId(int id) {
mId = id;
}
public String getName() {
return mName;
}
public void setName(String name) {
mName = name;
}
public List<String> getImageList() {
return mImageList;
}
public void setImageList(List<String> imageList) {
mImageList = imageList;
}
public int getAge() {
return mAge;
}
public void setAge(int age) {
mAge = age;
}
public String getNameAge() {
return String.format("%s, %d", getName(), getAge());
}
}
1.2. View custom
1.2.1. CircleIndicator
Vẽ và bắt sự kiện scroll viewpage và vẽ lại vị trí của ảnh tương ứng trong list image
package tuananh.com.slideimageinrecyclerview.view.custom;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.os.Build;
import android.support.annotation.AnimatorRes;
import android.support.annotation.DrawableRes;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
import tuananh.com.slideimageinrecyclerview.R;
import static android.support.v4.view.ViewPager.OnPageChangeListener;
public class CircleIndicator extends LinearLayout {
private final static int DEFAULT_INDICATOR_WIDTH = 5;
private ViewPager mViewpager;
private int mIndicatorMargin = -1;
private int mIndicatorWidth = -1;
private int mIndicatorHeight = -1;
private int mAnimatorResId = R.animator.scale_with_alpha;
private int mAnimatorReverseResId = 0;
private int mIndicatorBackgroundResId = R.drawable.blur_radius;
private int mIndicatorUnselectedBackgroundResId = R.drawable.blur_radius;
private Animator mAnimatorOut;
private Animator mAnimatorIn;
private Animator mImmediateAnimatorOut;
private Animator mImmediateAnimatorIn;
private int mLastPosition = -1;
private final OnPageChangeListener mInternalPageChangeListener = new OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if (mViewpager.getAdapter() == null || mViewpager.getAdapter().getCount() <= 0) {
return;
}
if (mAnimatorIn.isRunning()) {
mAnimatorIn.end();
mAnimatorIn.cancel();
}
if (mAnimatorOut.isRunning()) {
mAnimatorOut.end();
mAnimatorOut.cancel();
}
View currentIndicator;
if (mLastPosition >= 0 && (currentIndicator = getChildAt(mLastPosition)) != null) {
currentIndicator.setBackgroundResource(mIndicatorUnselectedBackgroundResId);
mAnimatorIn.setTarget(currentIndicator);
mAnimatorIn.start();
}
View selectedIndicator = getChildAt(position);
if (selectedIndicator != null) {
selectedIndicator.setBackgroundResource(mIndicatorBackgroundResId);
mAnimatorOut.setTarget(selectedIndicator);
mAnimatorOut.start();
}
mLastPosition = position;
}
@Override
public void onPageScrollStateChanged(int state) {
}
};
private DataSetObserver mInternalDataSetObserver = new DataSetObserver() {
@Override
public void onChanged() {
super.onChanged();
if (mViewpager == null) {
return;
}
int newCount = mViewpager.getAdapter().getCount();
int currentCount = getChildCount();
if (newCount == currentCount) { // No change
return;
} else if (mLastPosition < newCount) {
mLastPosition = mViewpager.getCurrentItem();
} else {
mLastPosition = -1;
}
createIndicators();
}
};
public CircleIndicator(Context context) {
super(context);
init(context, null);
}
public CircleIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public CircleIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CircleIndicator(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
handleTypedArray(context, attrs);
checkIndicatorConfig(context);
}
private void handleTypedArray(Context context, AttributeSet attrs) {
if (attrs == null) {
return;
}
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleIndicator);
mIndicatorWidth =
typedArray.getDimensionPixelSize(R.styleable.CircleIndicator_ci_width, -1);
mIndicatorHeight =
typedArray.getDimensionPixelSize(R.styleable.CircleIndicator_ci_height, -1);
mIndicatorMargin =
typedArray.getDimensionPixelSize(R.styleable.CircleIndicator_ci_margin, -1);
mAnimatorResId = typedArray.getResourceId(R.styleable.CircleIndicator_ci_animator,
R.animator.scale_with_alpha);
mAnimatorReverseResId =
typedArray.getResourceId(R.styleable.CircleIndicator_ci_animator_reverse, 0);
mIndicatorBackgroundResId =
typedArray.getResourceId(R.styleable.CircleIndicator_ci_drawable,
R.drawable.blur_radius);
mIndicatorUnselectedBackgroundResId =
typedArray.getResourceId(R.styleable.CircleIndicator_ci_drawable_unselected,
mIndicatorBackgroundResId);
int orientation = typedArray.getInt(R.styleable.CircleIndicator_ci_orientation, -1);
setOrientation(orientation == VERTICAL ? VERTICAL : HORIZONTAL);
int gravity = typedArray.getInt(R.styleable.CircleIndicator_ci_gravity, -1);
setGravity(gravity >= 0 ? gravity : Gravity.CENTER);
typedArray.recycle();
}
/**
* Create and configure Indicator in Java code.
*/
public void configureIndicator(int indicatorWidth, int indicatorHeight, int indicatorMargin) {
configureIndicator(indicatorWidth, indicatorHeight, indicatorMargin,
R.animator.scale_with_alpha, 0, R.drawable.blur_radius, R.drawable.blur_radius);
}
public void configureIndicator(int indicatorWidth, int indicatorHeight, int indicatorMargin,
@AnimatorRes int animatorId, @AnimatorRes int animatorReverseId,
@DrawableRes int indicatorBackgroundId,
@DrawableRes int indicatorUnselectedBackgroundId) {
mIndicatorWidth = indicatorWidth;
mIndicatorHeight = indicatorHeight;
mIndicatorMargin = indicatorMargin;
mAnimatorResId = animatorId;
mAnimatorReverseResId = animatorReverseId;
mIndicatorBackgroundResId = indicatorBackgroundId;
mIndicatorUnselectedBackgroundResId = indicatorUnselectedBackgroundId;
checkIndicatorConfig(getContext());
}
private void checkIndicatorConfig(Context context) {
mIndicatorWidth = (mIndicatorWidth < 0) ? dip2px(DEFAULT_INDICATOR_WIDTH) : mIndicatorWidth;
mIndicatorHeight =
(mIndicatorHeight < 0) ? dip2px(DEFAULT_INDICATOR_WIDTH) : mIndicatorHeight;
mIndicatorMargin =
(mIndicatorMargin < 0) ? dip2px(DEFAULT_INDICATOR_WIDTH) : mIndicatorMargin;
mAnimatorResId = (mAnimatorResId == 0) ? R.animator.scale_with_alpha : mAnimatorResId;
mAnimatorOut = createAnimatorOut(context);
mImmediateAnimatorOut = createAnimatorOut(context);
mImmediateAnimatorOut.setDuration(0);
mAnimatorIn = createAnimatorIn(context);
mImmediateAnimatorIn = createAnimatorIn(context);
mImmediateAnimatorIn.setDuration(0);
mIndicatorBackgroundResId = (mIndicatorBackgroundResId == 0) ? R.drawable.blur_radius
: mIndicatorBackgroundResId;
mIndicatorUnselectedBackgroundResId =
(mIndicatorUnselectedBackgroundResId == 0) ? mIndicatorBackgroundResId
: mIndicatorUnselectedBackgroundResId;
}
private Animator createAnimatorOut(Context context) {
return AnimatorInflater.loadAnimator(context, mAnimatorResId);
}
private Animator createAnimatorIn(Context context) {
Animator animatorIn;
if (mAnimatorReverseResId == 0) {
animatorIn = AnimatorInflater.loadAnimator(context, mAnimatorResId);
animatorIn.setInterpolator(new ReverseInterpolator());
} else {
animatorIn = AnimatorInflater.loadAnimator(context, mAnimatorReverseResId);
}
return animatorIn;
}
public void setViewPager(ViewPager viewPager) {
mViewpager = viewPager;
if (mViewpager != null && mViewpager.getAdapter() != null) {
mLastPosition = -1;
createIndicators();
mViewpager.removeOnPageChangeListener(mInternalPageChangeListener);
mViewpager.addOnPageChangeListener(mInternalPageChangeListener);
mInternalPageChangeListener.onPageSelected(mViewpager.getCurrentItem());
}
}
public DataSetObserver getDataSetObserver() {
return mInternalDataSetObserver;
}
/**
* @deprecated User ViewPager addOnPageChangeListener
*/
@Deprecated
public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
if (mViewpager == null) {
throw new NullPointerException("can not find Viewpager , setViewPager first");
}
mViewpager.removeOnPageChangeListener(onPageChangeListener);
mViewpager.addOnPageChangeListener(onPageChangeListener);
}
private void createIndicators() {
removeAllViews();
int count = mViewpager.getAdapter().getCount();
if (count <= 0) {
return;
}
int currentItem = mViewpager.getCurrentItem();
int orientation = getOrientation();
for (int i = 0; i < count; i++) {
if (currentItem == i) {
addIndicator(orientation, mIndicatorBackgroundResId, mImmediateAnimatorOut);
} else {
addIndicator(orientation, mIndicatorUnselectedBackgroundResId,
mImmediateAnimatorIn);
}
}
}
private void addIndicator(int orientation, @DrawableRes int backgroundDrawableId,
Animator animator) {
if (animator.isRunning()) {
animator.end();
animator.cancel();
}
View Indicator = new View(getContext());
Indicator.setBackgroundResource(backgroundDrawableId);
addView(Indicator, mIndicatorWidth, mIndicatorHeight);
LayoutParams lp = (LayoutParams) Indicator.getLayoutParams();
if (orientation == HORIZONTAL) {
lp.leftMargin = mIndicatorMargin;
lp.rightMargin = mIndicatorMargin;
} else {
lp.topMargin = mIndicatorMargin;
lp.bottomMargin = mIndicatorMargin;
}
Indicator.setLayoutParams(lp);
animator.setTarget(Indicator);
animator.start();
}
public int dip2px(float dpValue) {
final float scale = getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
private class ReverseInterpolator implements Interpolator {
@Override
public float getInterpolation(float value) {
return Math.abs(1.0f - value);
}
}
}
1.2.2 ResizableImageView
Resize Image theo chiều rộng mà ko vỡ ảnh
package tuananh.com.slideimageinrecyclerview.view.custom;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
/**
* Created by TuanAnh on 4/28/2017.
*/
public class ResizableImageView extends ImageView {
public ResizableImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Drawable d = getDrawable();
if (d != null) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = 0;
height = (int) Math.ceil((float) width
* (float) d.getIntrinsicHeight()
/ (float) d.getIntrinsicWidth());
setMeasuredDimension(width, height);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
1.3. Tạo class create data
Khởi tạo list profile cho ứng dụng
package tuananh.com.slideimageinrecyclerview;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import tuananh.com.slideimageinrecyclerview.model.Profile;
/**
* Created by framgia on 27/04/2017.
*/
public class CreateData {
public static List<Profile> createProfile() {
List<Profile> profileList = new ArrayList<>();
profileList.add(create(1, "Đào", 22, Arrays.asList(
"http://imageshack.com/a/img922/986/1C5zeO.jpg",
"http://imageshack.com/a/img924/224/taTtUg.jpg",
"http://imageshack.com/a/img923/1333/cnHzDa.jpg",
"http://imageshack.com/a/img922/9823/TpA14D.jpg",
"http://imageshack.com/a/img924/4017/f9pqLq.jpg",
"http://imageshack.com/a/img922/8958/4KAmdD.jpg",
"http://imageshack.com/a/img923/8135/nyfIov.jpg")));
profileList.add(create(2, "Đào", 22, Arrays.asList(
"http://imageshack.com/a/img923/2374/jBIAqT.jpg",
"http://imageshack.com/a/img923/9599/ulegwM.jpg",
"http://imageshack.com/a/img923/7016/HzEvo8.jpg",
"http://imageshack.com/a/img922/1641/emfvPy.jpg",
"http://imageshack.com/a/img923/116/yfyetV.jpg",
"http://imageshack.com/a/img924/4846/zveut5.jpg",
"http://imageshack.com/a/img923/472/SB341D.jpg",
"http://imageshack.com/a/img924/4350/5ghok1.jpg")));
profileList.add(create(3, "Đào", 22, Arrays.asList(
"http://imageshack.com/a/img923/3835/q79WKE.jpg",
"http://imageshack.com/a/img922/3615/sNKPpV.jpg",
"http://imageshack.com/a/img924/7272/UXID41.jpg",
"http://imageshack.com/a/img923/1332/sTx6qw.jpg",
"http://imageshack.com/a/img923/3338/v0hyg5.jpg",
"http://imageshack.com/a/img922/8435/puIYQa.jpg")));
profileList.add(create(4, "Đào", 22, Arrays.asList(
"http://imageshack.com/a/img922/3390/GXOW9e.jpg",
"http://imageshack.com/a/img922/8017/Qnxgbw.jpg")));
profileList.add(create(5, "Đào", 22, Arrays.asList(
"http://imageshack.com/a/img924/331/ujExV6.jpg",
"http://imageshack.com/a/img923/9503/qgUaHN.jpg",
"http://imageshack.com/a/img924/5319/RNBJQp.jpg",
"http://imageshack.com/a/img923/3299/nWdFeZ.jpg",
"http://imageshack.com/a/img922/461/iIGnvU.jpg",
"http://imageshack.com/a/img922/3748/XvWQPz.jpg",
"http://imageshack.com/a/img922/2177/gm9ddy.jpg",
"http://imageshack.com/a/img924/7344/JBqVbQ.jpg")));
profileList.add(create(6, "Đào", 22, Arrays.asList(
"http://imageshack.com/a/img924/6515/JiylGo.jpg",
"http://imageshack.com/a/img924/6537/AfNnra.jpg",
"http://imageshack.com/a/img924/802/rBCWs3.jpg",
"http://imageshack.com/a/img923/9322/bO0hYw.jpg",
"http://imageshack.com/a/img923/7392/kyiKsh.jpg")));
profileList.add(create(7, "Đào", 22, Arrays.asList(
"http://imageshack.com/a/img924/9423/E8Xxky.jpg",
"http://imageshack.com/a/img922/8366/7TVQZk.jpg",
"http://imageshack.com/a/img922/8117/3OJydD.jpg")));
profileList.add(create(8, "Đào", 22, Arrays.asList(
"http://imageshack.com/a/img923/6430/OK2kfH.jpg",
"http://imageshack.com/a/img924/8688/j6RaJz.jpg",
"http://imageshack.com/a/img924/7714/LWTaxv.jpg",
"http://imageshack.com/a/img922/64/u9LXlR.jpg")));
return profileList;
}
public static Profile create(int id, String name, int age, List<String> imageList) {
return new Profile(id, name, age, imageList);
}
}
1.4. ViewBindingAdapter
Function loadImage() để vẽ ảnh bằng picasso với link url đc truyền vào
package tuananh.com.slideimageinrecyclerview.view.binding;
import android.databinding.BindingAdapter;
import android.widget.ImageView;
import com.squareup.picasso.Picasso;
import tuananh.com.slideimageinrecyclerview.R;
/**
* Created by framgia on 27/04/2017.
*/
public class ViewBindingAdapter {
@BindingAdapter("imageUrl")
public static void loadImage(ImageView imageView, String url) {
Picasso.with(imageView.getContext())
.load(url)
.placeholder(R.drawable.image_default)
.into(imageView);
}
}
1.5. Interface OnClickShowImageListener
package tuananh.com.slideimageinrecyclerview.listener;
import tuananh.com.slideimageinrecyclerview.model.Profile;
/**
* Created by TuanAnh on 4/28/2017.
*/
public interface OnClickShowImageListener {
void onClickShowImage(Profile profile, int positionImage);
}
2. Adapter
2.1 SlideImageAdapter
2.1.1. SlideImageAdapter
Tạo Adapter cho slide image là SlideImageAdapter
Hàm khởi tạo cần truyền profile và listener onClickShowImageListener ( mục đích là bắt sự kiện OnClickListener vào ảnh để sang trang show full image)
public SlideImageAdapter(Context context, Profile profile,
OnClickShowImageListener onClickShowImageListener) {
}
package tuananh.com.slideimageinrecyclerview.view.adapter;
import android.content.Context;
import android.databinding.DataBindingUtil;
import android.support.v4.view.PagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
import tuananh.com.slideimageinrecyclerview.R;
import tuananh.com.slideimageinrecyclerview.databinding.FragmentSlideProfileBinding;
import tuananh.com.slideimageinrecyclerview.listener.OnClickShowImageListener;
import tuananh.com.slideimageinrecyclerview.model.Profile;
/**
* Created by framgia on 27/04/2017.
*/
public class SlideImageAdapter extends PagerAdapter {
private Context mContext;
private List<String> mImageList;
private LayoutInflater mLayoutInflater;
private Profile mProfile;
private OnClickShowImageListener mOnClickShowImageListener;
public SlideImageAdapter(Context context, Profile profile,
OnClickShowImageListener onClickShowImageListener) {
mContext = context;
mProfile = profile;
mImageList = profile.getImageList();
mOnClickShowImageListener = onClickShowImageListener;
mLayoutInflater = LayoutInflater.from(context);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
@Override
public int getCount() {
return mImageList == null ? 0 : mImageList.size();
}
@Override
public Object instantiateItem(ViewGroup container, final int position) {
FragmentSlideProfileBinding binding = DataBindingUtil.inflate(mLayoutInflater,
R.layout.fragment_slide_profile, container, false);
binding.getRoot().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnClickShowImageListener != null) {
mOnClickShowImageListener.onClickShowImage(mProfile, position);
}
}
});
binding.setUrl(mImageList.get(position));
binding.setPosition(position);
binding.getRoot().setTag(position);
container.addView(binding.getRoot(), 0);
return binding.getRoot();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view.equals(object);
}
}
2.1.2. Layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="position"
type="int"/>
<variable
name="url"
type="String"/>
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<ImageView
android:id="@+id/image_slide"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:imageUrl="@{url}"/>
</FrameLayout>
</layout>
2.2. ProfileAdapter
2.2.1. Tạo adapter ProfileAdapter cho reycerview
Function public void bind(Profile profile) {}
- mBinding.setVariable(BR.viewModel, profile); -> truyền data và trong layout
- SlideImageAdapter slideImageAdapter =
new SlideImageAdapter(mContext, profile, mOnClickShowImageListener);
-> khởi tạo slideImageAdapter cho viewpager trong mỗi item của recyclerview - mBinding.slideImageViewPager.setAdapter(slideImageAdapter); -> truyền adapter vào viewpager
- mBinding.slideImageViewPager.setOffscreenPageLimit(MAX_SIZE_IMAGE_LOAD); -> load tối đa MAX_SIZE_IMAGE_LOAD ảnh ban đầu
- mBinding.indicator.setViewPager(mBinding.slideImageViewPager); -> truyền viewpager vào CircleIndicator để bắt sự kiện scroll viewpager
package tuananh.com.slideimageinrecyclerview.view.adapter;
import android.content.Context;
import android.databinding.DataBindingUtil;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import java.util.List;
import tuananh.com.slideimageinrecyclerview.BR;
import tuananh.com.slideimageinrecyclerview.R;
import tuananh.com.slideimageinrecyclerview.databinding.ItemRecyclerProfileBinding;
import tuananh.com.slideimageinrecyclerview.listener.OnClickShowImageListener;
import tuananh.com.slideimageinrecyclerview.model.Profile;
/**
* Created by TuanAnh on 4/26/2017.
*/
public class ProfileAdapter extends RecyclerView.Adapter<ProfileAdapter.ProfileViewHolder> {
private static final int MAX_SIZE_IMAGE_LOAD = 8;
private Context mContext;
private LayoutInflater mLayoutInflater;
private List<Profile> mProfileList;
private OnClickShowImageListener mOnClickShowImageListener;
public ProfileAdapter(Context context, List<Profile> profileList,
OnClickShowImageListener onClickShowImageListener) {
mContext = context;
mProfileList = profileList;
mOnClickShowImageListener = onClickShowImageListener;
mLayoutInflater = LayoutInflater.from(context);
}
@Override
public ProfileViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ItemRecyclerProfileBinding binding = DataBindingUtil.inflate(mLayoutInflater,
R.layout.item_recycler_profile, parent, false);
return new ProfileViewHolder(binding);
}
@Override
public void onBindViewHolder(ProfileViewHolder holder, int position) {
Profile profile = mProfileList.get(position);
holder.bind(profile);
}
@Override
public int getItemCount() {
return mProfileList == null ? 0 : mProfileList.size();
}
public class ProfileViewHolder extends RecyclerView.ViewHolder {
private ItemRecyclerProfileBinding mBinding;
public ProfileViewHolder(ItemRecyclerProfileBinding binding) {
super(binding.getRoot());
mBinding = binding;
}
public void bind(Profile profile) {
mBinding.setVariable(BR.viewModel, profile);
SlideImageAdapter slideImageAdapter =
new SlideImageAdapter(mContext, profile, mOnClickShowImageListener);
mBinding.sliderImageViewPager.setAdapter(slideImageAdapter);
mBinding.sliderImageViewPager.setOffscreenPageLimit(MAX_SIZE_IMAGE_LOAD);
mBinding.indicator.setViewPager(mBinding.sliderImageViewPager);
mBinding.executePendingBindings();
}
}
}
2.2.2. Layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="tuananh.com.slideimageinrecyclerview.model.Profile"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_padding_10"
android:layout_marginLeft="@dimen/margin_padding_20"
android:drawableLeft="@drawable/img_dots"
android:drawablePadding="@dimen/margin_padding_10"
android:text="@{viewModel.nameAge}"
android:textSize="@dimen/text_size_14"
android:textStyle="bold"/>
<android.support.v4.view.ViewPager
android:id="@+id/slide_image_view_pager"
android:layout_width="match_parent"
android:layout_height="@dimen/size_200"/>
<tuananh.com.slideimageinrecyclerview.view.custom.CircleIndicator
android:id="@+id/indicator"
android:layout_width="match_parent"
android:layout_height="@dimen/size_30"
android:layout_gravity="bottom"
app:ci_drawable="@drawable/black_radius"
app:ci_drawable_unselected="@drawable/blur_radius"/>
</LinearLayout>
</layout>
Bài viết liên quan
[CSF-2] Một số thiết lập CSF, LFD
Hôm nay mình sẽ thực hiện một số thiết lập trên CSF Mở file config để sửa đổi một số tính năng dưới /etc/csf/csf.conf Nội dung chính1. Model, data, view, interface1.1. Tạo model Profile1.2. View...
[CSF-1] Tăng bảo mật Server với ConfigServer Firewall (CSF)
Nội dung chính1. Model, data, view, interface1.1. Tạo model Profile1.2. View custom1.2.1. CircleIndicator1.2.2 ResizableImageView1.3. Tạo class create data1.4. ViewBindingAdapter1.5. Interface OnClickShowImageListener2. Adapter2.1 SlideImageAdapter2.1.1. SlideImageAdapter2.1.2. Layout2.2. ProfileAdapter2.2.1. Tạo adapter ProfileAdapter cho reycerview2.2.2. LayoutImageTham khảoCode1. Khái niệm...
Sử dụng SSH Key với Gitlab và Github
Bài viết này mình sẽ hướng dẫn các bạn tạo ssh key cho Gitlab và Github SSH là gì? Secure Socket Shell là một giao thức mạng dùng để thiết lập kết nối mạng một...
Directory traversal vulnerabilities (phần 4)
Nội dung chính1. Model, data, view, interface1.1. Tạo model Profile1.2. View custom1.2.1. CircleIndicator1.2.2 ResizableImageView1.3. Tạo class create data1.4. ViewBindingAdapter1.5. Interface OnClickShowImageListener2. Adapter2.1 SlideImageAdapter2.1.1. SlideImageAdapter2.1.2. Layout2.2. ProfileAdapter2.2.1. Tạo adapter ProfileAdapter cho reycerview2.2.2. LayoutImageTham khảoCodeV. Phân tích...
Directory traversal vulnerabilities (phần 3)
Nội dung chính1. Model, data, view, interface1.1. Tạo model Profile1.2. View custom1.2.1. CircleIndicator1.2.2 ResizableImageView1.3. Tạo class create data1.4. ViewBindingAdapter1.5. Interface OnClickShowImageListener2. Adapter2.1 SlideImageAdapter2.1.1. SlideImageAdapter2.1.2. Layout2.2. ProfileAdapter2.2.1. Tạo adapter ProfileAdapter cho reycerview2.2.2. LayoutImageTham khảoCodeV. Phân tích...
Directory traversal vulnerabilities (phần 2)
Nội dung chính1. Model, data, view, interface1.1. Tạo model Profile1.2. View custom1.2.1. CircleIndicator1.2.2 ResizableImageView1.3. Tạo class create data1.4. ViewBindingAdapter1.5. Interface OnClickShowImageListener2. Adapter2.1 SlideImageAdapter2.1.1. SlideImageAdapter2.1.2. Layout2.2. ProfileAdapter2.2.1. Tạo adapter ProfileAdapter cho reycerview2.2.2. LayoutImageTham khảoCodeIII. Vì sao...