@ -0,0 +1,102 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
|
||||
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.ScrollHelper;
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.ViewPagerLayoutManager;
|
||||
import com.yinuo.commonlibtest.R;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Dajavu on 26/10/2017.
|
||||
*/
|
||||
|
||||
public abstract class BaseActivity<V extends ViewPagerLayoutManager, S extends SettingPopUpWindow>
|
||||
extends AppCompatActivity {
|
||||
private RecyclerView recyclerView;
|
||||
private V viewPagerLayoutManager;
|
||||
private S settingPopUpWindow;
|
||||
|
||||
protected abstract V createLayoutManager();
|
||||
|
||||
protected abstract S createSettingPopUpWindow();
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_base);
|
||||
setTitle(getIntent().getCharSequenceExtra(VariousRvDemoActivity.INTENT_TITLE));
|
||||
recyclerView = findViewById(R.id.recycler);
|
||||
viewPagerLayoutManager = createLayoutManager();
|
||||
DataAdapter dataAdapter = new DataAdapter();
|
||||
dataAdapter.setOnItemClickListener(new DataAdapter.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(View v, int pos) {
|
||||
Toast.makeText(v.getContext(), "clicked:" + pos, Toast.LENGTH_SHORT).show();
|
||||
ScrollHelper.smoothScrollToTargetView(recyclerView, v);
|
||||
}
|
||||
});
|
||||
recyclerView.setAdapter(dataAdapter);
|
||||
recyclerView.setLayoutManager(viewPagerLayoutManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.settings, menu);
|
||||
MenuItem settings = menu.findItem(R.id.setting);
|
||||
VectorDrawableCompat settingIcon =
|
||||
VectorDrawableCompat.create(getResources(), R.drawable.ic_settings_white_48px, null);
|
||||
settings.setIcon(settingIcon);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.setting:
|
||||
showDialog();
|
||||
return true;
|
||||
case android.R.id.home:
|
||||
onBackPressed();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void showDialog() {
|
||||
if (settingPopUpWindow == null) {
|
||||
settingPopUpWindow = createSettingPopUpWindow();
|
||||
}
|
||||
settingPopUpWindow.showAtLocation(recyclerView, Gravity.CENTER, 0, 0);
|
||||
}
|
||||
|
||||
public V getViewPagerLayoutManager() {
|
||||
return viewPagerLayoutManager;
|
||||
}
|
||||
|
||||
public S getSettingPopUpWindow() {
|
||||
return settingPopUpWindow;
|
||||
}
|
||||
|
||||
public RecyclerView getRecyclerView() {
|
||||
return recyclerView;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (settingPopUpWindow != null && settingPopUpWindow.isShowing())
|
||||
settingPopUpWindow.dismiss();
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.yinuo.commonlibtest.R;
|
||||
|
||||
public class DataAdapter extends RecyclerView.Adapter<DataAdapter.ViewHolder> {
|
||||
public OnItemClickListener onItemClickListener;
|
||||
private int[] images = {R.drawable.item1, R.drawable.item2, R.drawable.item3,
|
||||
R.drawable.item4, R.drawable.item5, R.drawable.item6, R.drawable.item7,
|
||||
R.drawable.item8, R.drawable.item9};
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
holder.imageView.setImageResource(images[position]);
|
||||
holder.imageView.setTag(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return images.length;
|
||||
}
|
||||
|
||||
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
|
||||
this.onItemClickListener = onItemClickListener;
|
||||
}
|
||||
|
||||
public interface OnItemClickListener {
|
||||
void onItemClick(View v, int pos);
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
ImageView imageView;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
imageView = itemView.findViewById(R.id.image);
|
||||
itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (onItemClickListener != null) {
|
||||
onItemClickListener.onItemClick(v, getAdapterPosition());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
/**
|
||||
* Created by Dajavu on 26/10/2017.
|
||||
*/
|
||||
|
||||
public abstract class SettingPopUpWindow extends PopupWindow {
|
||||
public SettingPopUpWindow(Context context) {
|
||||
super(context);
|
||||
setOutsideTouchable(true);
|
||||
setWidth(Util.Dp2px(context, 320));
|
||||
setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Created by Dajavu on 25/10/2017.
|
||||
*/
|
||||
|
||||
public class Util {
|
||||
public static int Dp2px(Context context, float dp) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (dp * scale + 0.5f);
|
||||
}
|
||||
|
||||
public static String formatFloat(float value) {
|
||||
return String.format(Locale.getDefault(), "%.3f", value);
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.AppCompatButton;
|
||||
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.carousel.CarouselLayoutActivity;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.circle.CircleLayoutActivity;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.circlescale.CircleScaleLayoutActivity;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.gallery.GalleryLayoutActivity;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.rotate.RotateLayoutActivity;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.scale.ScaleLayoutActivity;
|
||||
import com.yinuo.commonlibtest.R;
|
||||
|
||||
|
||||
public class VariousRvDemoActivity extends AppCompatActivity implements View.OnClickListener {
|
||||
public final static String INTENT_TITLE = "title";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.variois_rv_layout);
|
||||
findViewById(R.id.bt_circle).setOnClickListener(this);
|
||||
findViewById(R.id.bt_circle_scale).setOnClickListener(this);
|
||||
findViewById(R.id.bt_elevate_scale).setOnClickListener(this);
|
||||
findViewById(R.id.bt_gallery).setOnClickListener(this);
|
||||
findViewById(R.id.bt_rotate).setOnClickListener(this);
|
||||
findViewById(R.id.bt_scale).setOnClickListener(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.bt_circle:
|
||||
startActivity(CircleLayoutActivity.class, v);
|
||||
break;
|
||||
case R.id.bt_circle_scale:
|
||||
startActivity(CircleScaleLayoutActivity.class, v);
|
||||
break;
|
||||
case R.id.bt_elevate_scale:
|
||||
startActivity(CarouselLayoutActivity.class, v);
|
||||
break;
|
||||
case R.id.bt_gallery:
|
||||
startActivity(GalleryLayoutActivity.class, v);
|
||||
break;
|
||||
case R.id.bt_rotate:
|
||||
startActivity(RotateLayoutActivity.class, v);
|
||||
break;
|
||||
case R.id.bt_scale:
|
||||
startActivity(ScaleLayoutActivity.class, v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void startActivity(Class clz, View view) {
|
||||
Intent intent = new Intent(this, clz);
|
||||
if (view instanceof AppCompatButton) {
|
||||
intent.putExtra(INTENT_TITLE, ((AppCompatButton) view).getText());
|
||||
}
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager.carousel;
|
||||
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.CarouselLayoutManager;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.BaseActivity;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.Util;
|
||||
|
||||
public class CarouselLayoutActivity extends BaseActivity<CarouselLayoutManager, CarouselPopUpWindow> {
|
||||
|
||||
@Override
|
||||
protected CarouselLayoutManager createLayoutManager() {
|
||||
return new CarouselLayoutManager(this, Util.Dp2px(this, 100));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CarouselPopUpWindow createSettingPopUpWindow() {
|
||||
return new CarouselPopUpWindow(this, getViewPagerLayoutManager(), getRecyclerView());
|
||||
}
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager.carousel;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.CarouselLayoutManager;
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.SettingPopUpWindow;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.Util;
|
||||
import com.yinuo.commonlibtest.R;
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
public class CarouselPopUpWindow extends SettingPopUpWindow
|
||||
implements SeekBar.OnSeekBarChangeListener, CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
private CarouselLayoutManager carouselLayoutManager;
|
||||
private RecyclerView recyclerView;
|
||||
private TextView itemSpaceValue;
|
||||
private TextView speedValue;
|
||||
private TextView minScaleValue;
|
||||
private SwitchCompat changeOrientation;
|
||||
private SwitchCompat autoCenter;
|
||||
private SwitchCompat infinite;
|
||||
private SwitchCompat reverse;
|
||||
private CenterSnapHelper centerSnapHelper;
|
||||
|
||||
CarouselPopUpWindow(Context context, CarouselLayoutManager carouselLayoutManager, RecyclerView recyclerView) {
|
||||
super(context);
|
||||
this.carouselLayoutManager = carouselLayoutManager;
|
||||
this.recyclerView = recyclerView;
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.dialog_carousel_setting, null);
|
||||
setContentView(view);
|
||||
|
||||
centerSnapHelper = new CenterSnapHelper();
|
||||
|
||||
SeekBar itemSpace = view.findViewById(R.id.sb_item_space);
|
||||
SeekBar speed = view.findViewById(R.id.sb_speed);
|
||||
SeekBar minScale = view.findViewById(R.id.sb_min_scale);
|
||||
|
||||
itemSpaceValue = view.findViewById(R.id.item_space);
|
||||
speedValue = view.findViewById(R.id.speed_value);
|
||||
minScaleValue = view.findViewById(R.id.min_scale_value);
|
||||
|
||||
changeOrientation = view.findViewById(R.id.s_change_orientation);
|
||||
autoCenter = view.findViewById(R.id.s_auto_center);
|
||||
infinite = view.findViewById(R.id.s_infinite);
|
||||
reverse = view.findViewById(R.id.s_reverse);
|
||||
|
||||
itemSpace.setOnSeekBarChangeListener(this);
|
||||
speed.setOnSeekBarChangeListener(this);
|
||||
minScale.setOnSeekBarChangeListener(this);
|
||||
|
||||
itemSpace.setProgress(carouselLayoutManager.getItemSpace() / 5);
|
||||
speed.setProgress(Math.round(carouselLayoutManager.getMoveSpeed() / 0.05f));
|
||||
minScale.setProgress(Math.round(carouselLayoutManager.getMinScale() * 100));
|
||||
|
||||
itemSpaceValue.setText(String.valueOf(carouselLayoutManager.getItemSpace()));
|
||||
speedValue.setText(Util.formatFloat(carouselLayoutManager.getMoveSpeed()));
|
||||
minScaleValue.setText(Util.formatFloat(carouselLayoutManager.getMinScale()));
|
||||
|
||||
changeOrientation.setChecked(carouselLayoutManager.getOrientation() == RecyclerView.VERTICAL);
|
||||
reverse.setChecked(carouselLayoutManager.getReverseLayout());
|
||||
infinite.setChecked(carouselLayoutManager.getInfinite());
|
||||
|
||||
changeOrientation.setOnCheckedChangeListener(this);
|
||||
autoCenter.setOnCheckedChangeListener(this);
|
||||
reverse.setOnCheckedChangeListener(this);
|
||||
infinite.setOnCheckedChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
switch (seekBar.getId()) {
|
||||
case R.id.sb_item_space:
|
||||
int itemSpace = progress * 5;
|
||||
carouselLayoutManager.setItemSpace(itemSpace);
|
||||
itemSpaceValue.setText(String.valueOf(itemSpace));
|
||||
break;
|
||||
case R.id.sb_min_scale:
|
||||
final float scale = progress / 100f;
|
||||
carouselLayoutManager.setMinScale(scale);
|
||||
minScaleValue.setText(Util.formatFloat(scale));
|
||||
break;
|
||||
case R.id.sb_speed:
|
||||
final float speed = progress * 0.05f;
|
||||
carouselLayoutManager.setMoveSpeed(speed);
|
||||
speedValue.setText(Util.formatFloat(speed));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
switch (buttonView.getId()) {
|
||||
case R.id.s_infinite:
|
||||
recyclerView.scrollToPosition(0);
|
||||
carouselLayoutManager.setInfinite(isChecked);
|
||||
break;
|
||||
case R.id.s_change_orientation:
|
||||
carouselLayoutManager.scrollToPosition(0);
|
||||
carouselLayoutManager.setOrientation(isChecked ?
|
||||
RecyclerView.VERTICAL : RecyclerView.HORIZONTAL);
|
||||
break;
|
||||
case R.id.s_auto_center:
|
||||
if (isChecked) {
|
||||
centerSnapHelper.attachToRecyclerView(recyclerView);
|
||||
} else {
|
||||
centerSnapHelper.attachToRecyclerView(null);
|
||||
}
|
||||
break;
|
||||
case R.id.s_reverse:
|
||||
carouselLayoutManager.scrollToPosition(0);
|
||||
carouselLayoutManager.setReverseLayout(isChecked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager.circle;
|
||||
|
||||
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.CircleLayoutManager;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.BaseActivity;
|
||||
|
||||
/**
|
||||
* Created by Dajavu on 25/10/2017.
|
||||
*/
|
||||
|
||||
public class CircleLayoutActivity extends BaseActivity<CircleLayoutManager, CirclePopUpWindow> {
|
||||
|
||||
@Override
|
||||
protected CircleLayoutManager createLayoutManager() {
|
||||
return new CircleLayoutManager(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CirclePopUpWindow createSettingPopUpWindow() {
|
||||
return new CirclePopUpWindow(this, getViewPagerLayoutManager(), getRecyclerView());
|
||||
}
|
||||
}
|
@ -0,0 +1,216 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager.circle;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper;
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.CircleLayoutManager;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.SettingPopUpWindow;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.Util;
|
||||
import com.yinuo.commonlibtest.R;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Dajavu on 25/10/2017.
|
||||
*/
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
class CirclePopUpWindow extends SettingPopUpWindow
|
||||
implements SeekBar.OnSeekBarChangeListener, CompoundButton.OnCheckedChangeListener,
|
||||
RadioGroup.OnCheckedChangeListener {
|
||||
|
||||
private CircleLayoutManager circleLayoutManager;
|
||||
private RecyclerView recyclerView;
|
||||
private TextView radiusValue;
|
||||
private TextView intervalValue;
|
||||
private TextView speedValue;
|
||||
private TextView distanceToBottomValue;
|
||||
private SwitchCompat infinite;
|
||||
private SwitchCompat autoCenter;
|
||||
private SwitchCompat reverse;
|
||||
private SwitchCompat flipRotate;
|
||||
private CenterSnapHelper centerSnapHelper;
|
||||
private RadioGroup gravity;
|
||||
private RadioGroup zAlignment;
|
||||
|
||||
CirclePopUpWindow(Context context, CircleLayoutManager circleLayoutManager, RecyclerView recyclerView) {
|
||||
super(context);
|
||||
this.circleLayoutManager = circleLayoutManager;
|
||||
this.recyclerView = recyclerView;
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.dialog_circle_setting, null);
|
||||
setContentView(view);
|
||||
|
||||
centerSnapHelper = new CenterSnapHelper();
|
||||
|
||||
SeekBar radius = view.findViewById(R.id.sb_radius);
|
||||
SeekBar interval = view.findViewById(R.id.sb_interval);
|
||||
SeekBar speed = view.findViewById(R.id.sb_speed);
|
||||
SeekBar distanceToBottom = view.findViewById(R.id.sb_distance_to_bottom);
|
||||
|
||||
radiusValue = view.findViewById(R.id.radius_value);
|
||||
intervalValue = view.findViewById(R.id.interval_value);
|
||||
speedValue = view.findViewById(R.id.speed_value);
|
||||
distanceToBottomValue = view.findViewById(R.id.distance_to_bottom_value);
|
||||
|
||||
infinite = view.findViewById(R.id.s_infinite);
|
||||
autoCenter = view.findViewById(R.id.s_auto_center);
|
||||
reverse = view.findViewById(R.id.s_reverse);
|
||||
flipRotate = view.findViewById(R.id.s_flip);
|
||||
|
||||
gravity = view.findViewById(R.id.rg_gravity);
|
||||
zAlignment = view.findViewById(R.id.rg_z_alignment);
|
||||
|
||||
radius.setOnSeekBarChangeListener(this);
|
||||
interval.setOnSeekBarChangeListener(this);
|
||||
speed.setOnSeekBarChangeListener(this);
|
||||
distanceToBottom.setOnSeekBarChangeListener(this);
|
||||
|
||||
final int maxRadius = Util.Dp2px(radius.getContext(), 400);
|
||||
radius.setProgress(Math.round(circleLayoutManager.getRadius() * 1f / maxRadius * 100));
|
||||
interval.setProgress(Math.round(circleLayoutManager.getAngleInterval() / 0.9f));
|
||||
speed.setProgress(Math.round(circleLayoutManager.getMoveSpeed() / 0.005f));
|
||||
distanceToBottom.setProgress(circleLayoutManager.getDistanceToBottom() / 10);
|
||||
|
||||
radiusValue.setText(String.valueOf(circleLayoutManager.getRadius()));
|
||||
intervalValue.setText(String.valueOf(circleLayoutManager.getAngleInterval()));
|
||||
speedValue.setText(Util.formatFloat(circleLayoutManager.getMoveSpeed()));
|
||||
distanceToBottomValue.setText(String.valueOf(circleLayoutManager.getDistanceToBottom()));
|
||||
|
||||
infinite.setChecked(circleLayoutManager.getInfinite());
|
||||
reverse.setChecked(circleLayoutManager.getReverseLayout());
|
||||
flipRotate.setChecked(circleLayoutManager.getFlipRotate());
|
||||
|
||||
infinite.setOnCheckedChangeListener(this);
|
||||
autoCenter.setOnCheckedChangeListener(this);
|
||||
reverse.setOnCheckedChangeListener(this);
|
||||
flipRotate.setOnCheckedChangeListener(this);
|
||||
|
||||
switch (circleLayoutManager.getGravity()) {
|
||||
case CircleLayoutManager.LEFT:
|
||||
gravity.check(R.id.rb_left);
|
||||
break;
|
||||
case CircleLayoutManager.RIGHT:
|
||||
gravity.check(R.id.rb_right);
|
||||
break;
|
||||
case CircleLayoutManager.TOP:
|
||||
gravity.check(R.id.rb_top);
|
||||
break;
|
||||
case CircleLayoutManager.BOTTOM:
|
||||
gravity.check(R.id.rb_bottom);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (circleLayoutManager.getZAlignment()) {
|
||||
case CircleLayoutManager.LEFT_ON_TOP:
|
||||
zAlignment.check(R.id.rb_left_on_top);
|
||||
break;
|
||||
case CircleLayoutManager.RIGHT_ON_TOP:
|
||||
zAlignment.check(R.id.rb_right_on_top);
|
||||
break;
|
||||
case CircleLayoutManager.CENTER_ON_TOP:
|
||||
zAlignment.check(R.id.rb_center_on_top);
|
||||
break;
|
||||
}
|
||||
|
||||
gravity.setOnCheckedChangeListener(this);
|
||||
zAlignment.setOnCheckedChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
switch (seekBar.getId()) {
|
||||
case R.id.sb_radius:
|
||||
final int maxRadius = Util.Dp2px(seekBar.getContext(), 400);
|
||||
final int radius = Math.round(progress / 100f * maxRadius);
|
||||
circleLayoutManager.setRadius(radius);
|
||||
radiusValue.setText(String.valueOf(radius));
|
||||
break;
|
||||
case R.id.sb_interval:
|
||||
final int interval = Math.round(progress * 0.9f);
|
||||
circleLayoutManager.setAngleInterval(interval);
|
||||
intervalValue.setText(String.valueOf(interval));
|
||||
break;
|
||||
case R.id.sb_speed:
|
||||
final float speed = progress * 0.005f;
|
||||
circleLayoutManager.setMoveSpeed(speed);
|
||||
speedValue.setText(Util.formatFloat(speed));
|
||||
break;
|
||||
case R.id.sb_distance_to_bottom:
|
||||
final int distance = progress * 10;
|
||||
circleLayoutManager.setDistanceToBottom(distance);
|
||||
distanceToBottomValue.setText(String.valueOf(distance));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
switch (buttonView.getId()) {
|
||||
case R.id.s_infinite:
|
||||
recyclerView.scrollToPosition(0);
|
||||
circleLayoutManager.setInfinite(isChecked);
|
||||
break;
|
||||
case R.id.s_auto_center:
|
||||
if (isChecked) {
|
||||
centerSnapHelper.attachToRecyclerView(recyclerView);
|
||||
} else {
|
||||
centerSnapHelper.attachToRecyclerView(null);
|
||||
}
|
||||
break;
|
||||
case R.id.s_reverse:
|
||||
circleLayoutManager.scrollToPosition(0);
|
||||
circleLayoutManager.setReverseLayout(isChecked);
|
||||
break;
|
||||
case R.id.s_flip:
|
||||
circleLayoutManager.setFlipRotate(isChecked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(RadioGroup group, int checkedId) {
|
||||
switch (checkedId) {
|
||||
case R.id.rb_left:
|
||||
circleLayoutManager.setGravity(CircleLayoutManager.LEFT);
|
||||
break;
|
||||
case R.id.rb_right:
|
||||
circleLayoutManager.setGravity(CircleLayoutManager.RIGHT);
|
||||
break;
|
||||
case R.id.rb_top:
|
||||
circleLayoutManager.setGravity(CircleLayoutManager.TOP);
|
||||
break;
|
||||
case R.id.rb_bottom:
|
||||
circleLayoutManager.setGravity(CircleLayoutManager.BOTTOM);
|
||||
break;
|
||||
case R.id.rb_left_on_top:
|
||||
circleLayoutManager.setZAlignment(CircleLayoutManager.LEFT_ON_TOP);
|
||||
break;
|
||||
case R.id.rb_right_on_top:
|
||||
circleLayoutManager.setZAlignment(CircleLayoutManager.RIGHT_ON_TOP);
|
||||
break;
|
||||
case R.id.rb_center_on_top:
|
||||
circleLayoutManager.setZAlignment(CircleLayoutManager.CENTER_ON_TOP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager.circlescale;
|
||||
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.CircleScaleLayoutManager;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.BaseActivity;
|
||||
|
||||
public class CircleScaleLayoutActivity extends BaseActivity<CircleScaleLayoutManager, CircleScalePopUpWindow> {
|
||||
|
||||
@Override
|
||||
protected CircleScaleLayoutManager createLayoutManager() {
|
||||
return new CircleScaleLayoutManager(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CircleScalePopUpWindow createSettingPopUpWindow() {
|
||||
return new CircleScalePopUpWindow(this, getViewPagerLayoutManager(), getRecyclerView());
|
||||
}
|
||||
}
|
@ -0,0 +1,211 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager.circlescale;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper;
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.CircleScaleLayoutManager;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.SettingPopUpWindow;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.Util;
|
||||
import com.yinuo.commonlibtest.R;
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
public class CircleScalePopUpWindow extends SettingPopUpWindow
|
||||
implements SeekBar.OnSeekBarChangeListener, CompoundButton.OnCheckedChangeListener,
|
||||
RadioGroup.OnCheckedChangeListener {
|
||||
|
||||
private CircleScaleLayoutManager circleScaleLayoutManager;
|
||||
private RecyclerView recyclerView;
|
||||
private TextView radiusValue;
|
||||
private TextView intervalValue;
|
||||
private TextView speedValue;
|
||||
private TextView centerScaleValue;
|
||||
private SwitchCompat infinite;
|
||||
private SwitchCompat autoCenter;
|
||||
private SwitchCompat reverse;
|
||||
private SwitchCompat flipRotate;
|
||||
private CenterSnapHelper centerSnapHelper;
|
||||
private RadioGroup gravity;
|
||||
private RadioGroup zAlignment;
|
||||
|
||||
CircleScalePopUpWindow(Context context, CircleScaleLayoutManager circleScaleLayoutManager, RecyclerView recyclerView) {
|
||||
super(context);
|
||||
this.circleScaleLayoutManager = circleScaleLayoutManager;
|
||||
this.recyclerView = recyclerView;
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.dialog_circle_scale_setting, null);
|
||||
setContentView(view);
|
||||
|
||||
centerSnapHelper = new CenterSnapHelper();
|
||||
|
||||
SeekBar radius = view.findViewById(R.id.sb_radius);
|
||||
SeekBar interval = view.findViewById(R.id.sb_interval);
|
||||
SeekBar speed = view.findViewById(R.id.sb_speed);
|
||||
SeekBar centerScale = view.findViewById(R.id.sb_center_scale);
|
||||
|
||||
radiusValue = view.findViewById(R.id.radius_value);
|
||||
intervalValue = view.findViewById(R.id.interval_value);
|
||||
speedValue = view.findViewById(R.id.speed_value);
|
||||
centerScaleValue = view.findViewById(R.id.center_scale_value);
|
||||
|
||||
infinite = view.findViewById(R.id.s_infinite);
|
||||
autoCenter = view.findViewById(R.id.s_auto_center);
|
||||
reverse = view.findViewById(R.id.s_reverse);
|
||||
flipRotate = view.findViewById(R.id.s_flip);
|
||||
|
||||
gravity = view.findViewById(R.id.rg_gravity);
|
||||
zAlignment = view.findViewById(R.id.rg_z_alignment);
|
||||
|
||||
radius.setOnSeekBarChangeListener(this);
|
||||
interval.setOnSeekBarChangeListener(this);
|
||||
speed.setOnSeekBarChangeListener(this);
|
||||
centerScale.setOnSeekBarChangeListener(this);
|
||||
|
||||
final int maxRadius = Util.Dp2px(radius.getContext(), 400);
|
||||
radius.setProgress(Math.round(circleScaleLayoutManager.getRadius() * 1f / maxRadius * 100));
|
||||
interval.setProgress(Math.round(circleScaleLayoutManager.getAngleInterval() / 0.9f));
|
||||
speed.setProgress(Math.round(circleScaleLayoutManager.getMoveSpeed() / 0.005f));
|
||||
centerScale.setProgress(Math.round(circleScaleLayoutManager.getCenterScale() * 200f / 3 - 100f / 3));
|
||||
|
||||
radiusValue.setText(String.valueOf(circleScaleLayoutManager.getRadius()));
|
||||
intervalValue.setText(String.valueOf(circleScaleLayoutManager.getAngleInterval()));
|
||||
speedValue.setText(Util.formatFloat(circleScaleLayoutManager.getMoveSpeed()));
|
||||
centerScaleValue.setText(Util.formatFloat(circleScaleLayoutManager.getCenterScale()));
|
||||
|
||||
infinite.setChecked(circleScaleLayoutManager.getInfinite());
|
||||
reverse.setChecked(circleScaleLayoutManager.getReverseLayout());
|
||||
flipRotate.setChecked(circleScaleLayoutManager.getFlipRotate());
|
||||
|
||||
infinite.setOnCheckedChangeListener(this);
|
||||
autoCenter.setOnCheckedChangeListener(this);
|
||||
reverse.setOnCheckedChangeListener(this);
|
||||
flipRotate.setOnCheckedChangeListener(this);
|
||||
|
||||
switch (circleScaleLayoutManager.getGravity()) {
|
||||
case CircleScaleLayoutManager.LEFT:
|
||||
gravity.check(R.id.rb_left);
|
||||
break;
|
||||
case CircleScaleLayoutManager.RIGHT:
|
||||
gravity.check(R.id.rb_right);
|
||||
break;
|
||||
case CircleScaleLayoutManager.TOP:
|
||||
gravity.check(R.id.rb_top);
|
||||
break;
|
||||
case CircleScaleLayoutManager.BOTTOM:
|
||||
gravity.check(R.id.rb_bottom);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (circleScaleLayoutManager.getZAlignment()) {
|
||||
case CircleScaleLayoutManager.LEFT_ON_TOP:
|
||||
zAlignment.check(R.id.rb_left_on_top);
|
||||
break;
|
||||
case CircleScaleLayoutManager.RIGHT_ON_TOP:
|
||||
zAlignment.check(R.id.rb_right_on_top);
|
||||
break;
|
||||
case CircleScaleLayoutManager.CENTER_ON_TOP:
|
||||
zAlignment.check(R.id.rb_center_on_top);
|
||||
break;
|
||||
}
|
||||
|
||||
gravity.setOnCheckedChangeListener(this);
|
||||
zAlignment.setOnCheckedChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
switch (seekBar.getId()) {
|
||||
case R.id.sb_radius:
|
||||
final int maxRadius = Util.Dp2px(seekBar.getContext(), 400);
|
||||
final int radius = Math.round(progress / 100f * maxRadius);
|
||||
circleScaleLayoutManager.setRadius(radius);
|
||||
radiusValue.setText(String.valueOf(radius));
|
||||
break;
|
||||
case R.id.sb_interval:
|
||||
final int interval = Math.round(progress * 0.9f);
|
||||
circleScaleLayoutManager.setAngleInterval(interval);
|
||||
intervalValue.setText(String.valueOf(interval));
|
||||
break;
|
||||
case R.id.sb_center_scale:
|
||||
final float scale = (progress + 100f / 3) * 3 / 200;
|
||||
circleScaleLayoutManager.setCenterScale(scale);
|
||||
centerScaleValue.setText(Util.formatFloat(scale));
|
||||
break;
|
||||
case R.id.sb_speed:
|
||||
final float speed = progress * 0.005f;
|
||||
circleScaleLayoutManager.setMoveSpeed(speed);
|
||||
speedValue.setText(Util.formatFloat(speed));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
switch (buttonView.getId()) {
|
||||
case R.id.s_infinite:
|
||||
recyclerView.scrollToPosition(0);
|
||||
circleScaleLayoutManager.setInfinite(isChecked);
|
||||
break;
|
||||
case R.id.s_auto_center:
|
||||
if (isChecked) {
|
||||
centerSnapHelper.attachToRecyclerView(recyclerView);
|
||||
} else {
|
||||
centerSnapHelper.attachToRecyclerView(null);
|
||||
}
|
||||
break;
|
||||
case R.id.s_reverse:
|
||||
circleScaleLayoutManager.scrollToPosition(0);
|
||||
circleScaleLayoutManager.setReverseLayout(isChecked);
|
||||
break;
|
||||
case R.id.s_flip:
|
||||
circleScaleLayoutManager.setFlipRotate(isChecked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(RadioGroup group, int checkedId) {
|
||||
switch (checkedId) {
|
||||
case R.id.rb_left:
|
||||
circleScaleLayoutManager.setGravity(CircleScaleLayoutManager.LEFT);
|
||||
break;
|
||||
case R.id.rb_right:
|
||||
circleScaleLayoutManager.setGravity(CircleScaleLayoutManager.RIGHT);
|
||||
break;
|
||||
case R.id.rb_top:
|
||||
circleScaleLayoutManager.setGravity(CircleScaleLayoutManager.TOP);
|
||||
break;
|
||||
case R.id.rb_bottom:
|
||||
circleScaleLayoutManager.setGravity(CircleScaleLayoutManager.BOTTOM);
|
||||
break;
|
||||
case R.id.rb_left_on_top:
|
||||
circleScaleLayoutManager.setZAlignment(CircleScaleLayoutManager.LEFT_ON_TOP);
|
||||
break;
|
||||
case R.id.rb_right_on_top:
|
||||
circleScaleLayoutManager.setZAlignment(CircleScaleLayoutManager.RIGHT_ON_TOP);
|
||||
break;
|
||||
case R.id.rb_center_on_top:
|
||||
circleScaleLayoutManager.setZAlignment(CircleScaleLayoutManager.CENTER_ON_TOP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager.gallery;
|
||||
|
||||
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.GalleryLayoutManager;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.BaseActivity;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.Util;
|
||||
|
||||
public class GalleryLayoutActivity extends BaseActivity<GalleryLayoutManager, GalleryPopUpWindow> {
|
||||
|
||||
@Override
|
||||
protected GalleryLayoutManager createLayoutManager() {
|
||||
return new GalleryLayoutManager(this, Util.Dp2px(this, 10));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GalleryPopUpWindow createSettingPopUpWindow() {
|
||||
return new GalleryPopUpWindow(this, getViewPagerLayoutManager(), getRecyclerView());
|
||||
}
|
||||
}
|
@ -0,0 +1,178 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager.gallery;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper;
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.GalleryLayoutManager;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.SettingPopUpWindow;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.Util;
|
||||
import com.yinuo.commonlibtest.R;
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
public class GalleryPopUpWindow extends SettingPopUpWindow
|
||||
implements SeekBar.OnSeekBarChangeListener, CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
private GalleryLayoutManager galleryLayoutManager;
|
||||
private RecyclerView recyclerView;
|
||||
private TextView itemSpaceValue;
|
||||
private TextView speedValue;
|
||||
private TextView minAlphaValue;
|
||||
private TextView maxAlphaValue;
|
||||
private TextView angleValue;
|
||||
private SwitchCompat centerInFront;
|
||||
private SwitchCompat changeOrientation;
|
||||
private SwitchCompat autoCenter;
|
||||
private SwitchCompat infinite;
|
||||
private SwitchCompat reverse;
|
||||
private SwitchCompat flipRotate;
|
||||
private SwitchCompat rotateFromEdge;
|
||||
private CenterSnapHelper centerSnapHelper;
|
||||
|
||||
GalleryPopUpWindow(Context context, GalleryLayoutManager galleryLayoutManager, RecyclerView recyclerView) {
|
||||
super(context);
|
||||
this.galleryLayoutManager = galleryLayoutManager;
|
||||
this.recyclerView = recyclerView;
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.dialog_gallery_setting, null);
|
||||
setContentView(view);
|
||||
|
||||
centerSnapHelper = new CenterSnapHelper();
|
||||
|
||||
SeekBar itemSpace = view.findViewById(R.id.sb_item_space);
|
||||
SeekBar speed = view.findViewById(R.id.sb_speed);
|
||||
SeekBar minAlpha = view.findViewById(R.id.sb_min_alpha);
|
||||
SeekBar maxAlpha = view.findViewById(R.id.sb_max_alpha);
|
||||
SeekBar angle = view.findViewById(R.id.sb_interval);
|
||||
|
||||
itemSpaceValue = view.findViewById(R.id.item_space);
|
||||
speedValue = view.findViewById(R.id.speed_value);
|
||||
minAlphaValue = view.findViewById(R.id.min_alpha_value);
|
||||
maxAlphaValue = view.findViewById(R.id.max_alpha_value);
|
||||
angleValue = view.findViewById(R.id.angle_value);
|
||||
|
||||
centerInFront = view.findViewById(R.id.s_center_in_front);
|
||||
changeOrientation = view.findViewById(R.id.s_change_orientation);
|
||||
autoCenter = view.findViewById(R.id.s_auto_center);
|
||||
infinite = view.findViewById(R.id.s_infinite);
|
||||
reverse = view.findViewById(R.id.s_reverse);
|
||||
flipRotate = view.findViewById(R.id.s_flip);
|
||||
rotateFromEdge = view.findViewById(R.id.s_rotate_from_edge);
|
||||
|
||||
itemSpace.setOnSeekBarChangeListener(this);
|
||||
speed.setOnSeekBarChangeListener(this);
|
||||
minAlpha.setOnSeekBarChangeListener(this);
|
||||
maxAlpha.setOnSeekBarChangeListener(this);
|
||||
angle.setOnSeekBarChangeListener(this);
|
||||
|
||||
itemSpace.setProgress(galleryLayoutManager.getItemSpace() / 8 + 50);
|
||||
speed.setProgress(Math.round(galleryLayoutManager.getMoveSpeed() / 0.05f));
|
||||
maxAlpha.setProgress(Math.round(galleryLayoutManager.getMaxAlpha() * 100));
|
||||
minAlpha.setProgress(Math.round(galleryLayoutManager.getMinAlpha() * 100));
|
||||
angle.setProgress(Math.round(galleryLayoutManager.getAngle() / 0.9f));
|
||||
|
||||
itemSpaceValue.setText(String.valueOf(galleryLayoutManager.getItemSpace()));
|
||||
speedValue.setText(Util.formatFloat(galleryLayoutManager.getMoveSpeed()));
|
||||
minAlphaValue.setText(Util.formatFloat(galleryLayoutManager.getMinAlpha()));
|
||||
maxAlphaValue.setText(Util.formatFloat(galleryLayoutManager.getMaxAlpha()));
|
||||
angleValue.setText(Util.formatFloat(galleryLayoutManager.getAngle()));
|
||||
|
||||
centerInFront.setChecked(galleryLayoutManager.getEnableBringCenterToFront());
|
||||
changeOrientation.setChecked(galleryLayoutManager.getOrientation() == RecyclerView.VERTICAL);
|
||||
reverse.setChecked(galleryLayoutManager.getReverseLayout());
|
||||
flipRotate.setChecked(galleryLayoutManager.getFlipRotate());
|
||||
rotateFromEdge.setChecked(galleryLayoutManager.getRotateFromEdge());
|
||||
infinite.setChecked(galleryLayoutManager.getInfinite());
|
||||
|
||||
centerInFront.setOnCheckedChangeListener(this);
|
||||
changeOrientation.setOnCheckedChangeListener(this);
|
||||
autoCenter.setOnCheckedChangeListener(this);
|
||||
reverse.setOnCheckedChangeListener(this);
|
||||
flipRotate.setOnCheckedChangeListener(this);
|
||||
rotateFromEdge.setOnCheckedChangeListener(this);
|
||||
infinite.setOnCheckedChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
switch (seekBar.getId()) {
|
||||
case R.id.sb_item_space:
|
||||
int itemSpace = (progress - 50) * 8;
|
||||
galleryLayoutManager.setItemSpace(itemSpace);
|
||||
itemSpaceValue.setText(String.valueOf(itemSpace));
|
||||
break;
|
||||
case R.id.sb_speed:
|
||||
final float speed = progress * 0.05f;
|
||||
galleryLayoutManager.setMoveSpeed(speed);
|
||||
speedValue.setText(Util.formatFloat(speed));
|
||||
break;
|
||||
case R.id.sb_interval:
|
||||
final int angle = Math.round(progress * 0.9f);
|
||||
galleryLayoutManager.setAngle(angle);
|
||||
angleValue.setText(String.valueOf(angle));
|
||||
break;
|
||||
case R.id.sb_max_alpha:
|
||||
final float maxAlpha = progress / 100f;
|
||||
galleryLayoutManager.setMaxAlpha(maxAlpha);
|
||||
maxAlphaValue.setText(Util.formatFloat(maxAlpha));
|
||||
break;
|
||||
case R.id.sb_min_alpha:
|
||||
final float minAlpha = progress / 100f;
|
||||
galleryLayoutManager.setMinAlpha(minAlpha);
|
||||
minAlphaValue.setText(Util.formatFloat(minAlpha));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
switch (buttonView.getId()) {
|
||||
case R.id.s_infinite:
|
||||
recyclerView.scrollToPosition(0);
|
||||
galleryLayoutManager.setInfinite(isChecked);
|
||||
break;
|
||||
case R.id.s_change_orientation:
|
||||
galleryLayoutManager.scrollToPosition(0);
|
||||
galleryLayoutManager.setOrientation(isChecked ?
|
||||
RecyclerView.VERTICAL : RecyclerView.HORIZONTAL);
|
||||
break;
|
||||
case R.id.s_auto_center:
|
||||
if (isChecked) {
|
||||
centerSnapHelper.attachToRecyclerView(recyclerView);
|
||||
} else {
|
||||
centerSnapHelper.attachToRecyclerView(null);
|
||||
}
|
||||
break;
|
||||
case R.id.s_reverse:
|
||||
galleryLayoutManager.scrollToPosition(0);
|
||||
galleryLayoutManager.setReverseLayout(isChecked);
|
||||
break;
|
||||
case R.id.s_flip:
|
||||
galleryLayoutManager.setFlipRotate(isChecked);
|
||||
case R.id.s_center_in_front:
|
||||
galleryLayoutManager.setEnableBringCenterToFront(isChecked);
|
||||
break;
|
||||
case R.id.s_rotate_from_edge:
|
||||
galleryLayoutManager.setRotateFromEdge(isChecked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager.rotate;
|
||||
|
||||
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.RotateLayoutManager;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.BaseActivity;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.Util;
|
||||
|
||||
public class RotateLayoutActivity extends BaseActivity<RotateLayoutManager, RotatePopUpWindow> {
|
||||
|
||||
@Override
|
||||
protected RotateLayoutManager createLayoutManager() {
|
||||
return new RotateLayoutManager(this, Util.Dp2px(this, 10));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RotatePopUpWindow createSettingPopUpWindow() {
|
||||
return new RotatePopUpWindow(this, getViewPagerLayoutManager(), getRecyclerView());
|
||||
}
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager.rotate;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper;
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.RotateLayoutManager;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.SettingPopUpWindow;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.Util;
|
||||
import com.yinuo.commonlibtest.R;
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
public class RotatePopUpWindow extends SettingPopUpWindow
|
||||
implements SeekBar.OnSeekBarChangeListener, CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
private RotateLayoutManager rotateLayoutManager;
|
||||
private RecyclerView recyclerView;
|
||||
private TextView itemSpaceValue;
|
||||
private TextView speedValue;
|
||||
private TextView angleValue;
|
||||
private SwitchCompat changeOrientation;
|
||||
private SwitchCompat autoCenter;
|
||||
private SwitchCompat infinite;
|
||||
private SwitchCompat reverseRotate;
|
||||
private SwitchCompat reverse;
|
||||
private CenterSnapHelper centerSnapHelper;
|
||||
|
||||
RotatePopUpWindow(Context context, RotateLayoutManager rotateLayoutManager, RecyclerView recyclerView) {
|
||||
super(context);
|
||||
this.rotateLayoutManager = rotateLayoutManager;
|
||||
this.recyclerView = recyclerView;
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.dialog_rotate_setting, null);
|
||||
setContentView(view);
|
||||
|
||||
centerSnapHelper = new CenterSnapHelper();
|
||||
|
||||
SeekBar itemSpace = view.findViewById(R.id.sb_item_space);
|
||||
SeekBar speed = view.findViewById(R.id.sb_speed);
|
||||
SeekBar angle = view.findViewById(R.id.sb_angle);
|
||||
|
||||
itemSpaceValue = view.findViewById(R.id.item_space);
|
||||
speedValue = view.findViewById(R.id.speed_value);
|
||||
angleValue = view.findViewById(R.id.angle_value);
|
||||
|
||||
reverseRotate = view.findViewById(R.id.s_reverse_rotate);
|
||||
changeOrientation = view.findViewById(R.id.s_change_orientation);
|
||||
autoCenter = view.findViewById(R.id.s_auto_center);
|
||||
infinite = view.findViewById(R.id.s_infinite);
|
||||
reverse = view.findViewById(R.id.s_reverse);
|
||||
|
||||
itemSpace.setOnSeekBarChangeListener(this);
|
||||
speed.setOnSeekBarChangeListener(this);
|
||||
angle.setOnSeekBarChangeListener(this);
|
||||
|
||||
itemSpace.setProgress(rotateLayoutManager.getItemSpace() / 2);
|
||||
speed.setProgress(Math.round(rotateLayoutManager.getMoveSpeed() / 0.05f));
|
||||
angle.setProgress(Math.round(rotateLayoutManager.getAngle() / 360 * 100));
|
||||
|
||||
itemSpaceValue.setText(String.valueOf(rotateLayoutManager.getItemSpace()));
|
||||
speedValue.setText(Util.formatFloat(rotateLayoutManager.getMoveSpeed()));
|
||||
angleValue.setText(Util.formatFloat(rotateLayoutManager.getAngle()));
|
||||
|
||||
reverseRotate.setChecked(rotateLayoutManager.getEnableBringCenterToFront());
|
||||
changeOrientation.setChecked(rotateLayoutManager.getOrientation() == RecyclerView.VERTICAL);
|
||||
reverse.setChecked(rotateLayoutManager.getReverseLayout());
|
||||
infinite.setChecked(rotateLayoutManager.getInfinite());
|
||||
|
||||
reverseRotate.setOnCheckedChangeListener(this);
|
||||
changeOrientation.setOnCheckedChangeListener(this);
|
||||
autoCenter.setOnCheckedChangeListener(this);
|
||||
reverse.setOnCheckedChangeListener(this);
|
||||
infinite.setOnCheckedChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
switch (seekBar.getId()) {
|
||||
case R.id.sb_item_space:
|
||||
int itemSpace = progress * 2;
|
||||
rotateLayoutManager.setItemSpace(itemSpace);
|
||||
itemSpaceValue.setText(String.valueOf(itemSpace));
|
||||
break;
|
||||
case R.id.sb_angle:
|
||||
final float angle = progress / 100f * 360;
|
||||
rotateLayoutManager.setAngle(angle);
|
||||
angleValue.setText(Util.formatFloat(angle));
|
||||
break;
|
||||
case R.id.sb_speed:
|
||||
final float speed = progress * 0.05f;
|
||||
rotateLayoutManager.setMoveSpeed(speed);
|
||||
speedValue.setText(Util.formatFloat(speed));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
switch (buttonView.getId()) {
|
||||
case R.id.s_infinite:
|
||||
recyclerView.scrollToPosition(0);
|
||||
rotateLayoutManager.setInfinite(isChecked);
|
||||
break;
|
||||
case R.id.s_change_orientation:
|
||||
rotateLayoutManager.scrollToPosition(0);
|
||||
rotateLayoutManager.setOrientation(isChecked ?
|
||||
RecyclerView.VERTICAL : RecyclerView.HORIZONTAL);
|
||||
break;
|
||||
case R.id.s_auto_center:
|
||||
if (isChecked) {
|
||||
centerSnapHelper.attachToRecyclerView(recyclerView);
|
||||
} else {
|
||||
centerSnapHelper.attachToRecyclerView(null);
|
||||
}
|
||||
break;
|
||||
case R.id.s_reverse_rotate:
|
||||
rotateLayoutManager.setReverseRotate(isChecked);
|
||||
break;
|
||||
case R.id.s_reverse:
|
||||
rotateLayoutManager.scrollToPosition(0);
|
||||
rotateLayoutManager.setReverseLayout(isChecked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager.scale;
|
||||
|
||||
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.ScaleLayoutManager;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.BaseActivity;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.Util;
|
||||
|
||||
public class ScaleLayoutActivity extends BaseActivity<ScaleLayoutManager, ScalePopUpWindow> {
|
||||
|
||||
@Override
|
||||
protected ScaleLayoutManager createLayoutManager() {
|
||||
return new ScaleLayoutManager(this, Util.Dp2px(this, 10));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ScalePopUpWindow createSettingPopUpWindow() {
|
||||
return new ScalePopUpWindow(this, getViewPagerLayoutManager(), getRecyclerView());
|
||||
}
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
package com.common.commonlibtest.viewpagerlayoutmanager.scale;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.CenterSnapHelper;
|
||||
import com.common.commonlib.view.viewpagerlayoutmanager.ScaleLayoutManager;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.SettingPopUpWindow;
|
||||
import com.common.commonlibtest.viewpagerlayoutmanager.Util;
|
||||
import com.yinuo.commonlibtest.R;
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
public class ScalePopUpWindow extends SettingPopUpWindow
|
||||
implements SeekBar.OnSeekBarChangeListener, CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
private ScaleLayoutManager scaleLayoutManager;
|
||||
private RecyclerView recyclerView;
|
||||
private TextView itemSpaceValue;
|
||||
private TextView speedValue;
|
||||
private TextView minScaleValue;
|
||||
private TextView minAlphaValue;
|
||||
private TextView maxAlphaValue;
|
||||
private SwitchCompat changeOrientation;
|
||||
private SwitchCompat autoCenter;
|
||||
private SwitchCompat infinite;
|
||||
private SwitchCompat reverse;
|
||||
private CenterSnapHelper centerSnapHelper;
|
||||
|
||||
ScalePopUpWindow(Context context, ScaleLayoutManager scaleLayoutManager, RecyclerView recyclerView) {
|
||||
super(context);
|
||||
this.scaleLayoutManager = scaleLayoutManager;
|
||||
this.recyclerView = recyclerView;
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.dialog_scale_setting, null);
|
||||
setContentView(view);
|
||||
|
||||
centerSnapHelper = new CenterSnapHelper();
|
||||
|
||||
SeekBar itemSpace = view.findViewById(R.id.sb_item_space);
|
||||
SeekBar speed = view.findViewById(R.id.sb_speed);
|
||||
SeekBar minScale = view.findViewById(R.id.sb_min_scale);
|
||||
SeekBar minAlpha = view.findViewById(R.id.sb_min_alpha);
|
||||
SeekBar maxAlpha = view.findViewById(R.id.sb_max_alpha);
|
||||
|
||||
itemSpaceValue = view.findViewById(R.id.item_space);
|
||||
speedValue = view.findViewById(R.id.speed_value);
|
||||
minScaleValue = view.findViewById(R.id.min_scale_value);
|
||||
minAlphaValue = view.findViewById(R.id.min_alpha_value);
|
||||
maxAlphaValue = view.findViewById(R.id.max_alpha_value);
|
||||
|
||||
changeOrientation = view.findViewById(R.id.s_change_orientation);
|
||||
autoCenter = view.findViewById(R.id.s_auto_center);
|
||||
infinite = view.findViewById(R.id.s_infinite);
|
||||
reverse = view.findViewById(R.id.s_reverse);
|
||||
|
||||
itemSpace.setOnSeekBarChangeListener(this);
|
||||
speed.setOnSeekBarChangeListener(this);
|
||||
minScale.setOnSeekBarChangeListener(this);
|
||||
minAlpha.setOnSeekBarChangeListener(this);
|
||||
maxAlpha.setOnSeekBarChangeListener(this);
|
||||
|
||||
itemSpace.setProgress(scaleLayoutManager.getItemSpace() / 2);
|
||||
speed.setProgress(Math.round(scaleLayoutManager.getMoveSpeed() / 0.05f));
|
||||
minScale.setProgress(Math.round((scaleLayoutManager.getMinScale() - 0.5f) * 200));
|
||||
maxAlpha.setProgress(Math.round(scaleLayoutManager.getMaxAlpha() * 100));
|
||||
minAlpha.setProgress(Math.round(scaleLayoutManager.getMinAlpha() * 100));
|
||||
|
||||
itemSpaceValue.setText(String.valueOf(scaleLayoutManager.getItemSpace()));
|
||||
speedValue.setText(Util.formatFloat(scaleLayoutManager.getMoveSpeed()));
|
||||
minScaleValue.setText(Util.formatFloat(scaleLayoutManager.getMinScale()));
|
||||
minAlphaValue.setText(Util.formatFloat(scaleLayoutManager.getMinAlpha()));
|
||||
maxAlphaValue.setText(Util.formatFloat(scaleLayoutManager.getMaxAlpha()));
|
||||
|
||||
changeOrientation.setChecked(scaleLayoutManager.getOrientation() == RecyclerView.VERTICAL);
|
||||
reverse.setChecked(scaleLayoutManager.getReverseLayout());
|
||||
infinite.setChecked(scaleLayoutManager.getInfinite());
|
||||
|
||||
changeOrientation.setOnCheckedChangeListener(this);
|
||||
autoCenter.setOnCheckedChangeListener(this);
|
||||
reverse.setOnCheckedChangeListener(this);
|
||||
infinite.setOnCheckedChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
switch (seekBar.getId()) {
|
||||
case R.id.sb_item_space:
|
||||
int itemSpace = progress * 2;
|
||||
scaleLayoutManager.setItemSpace(itemSpace);
|
||||
itemSpaceValue.setText(String.valueOf(itemSpace));
|
||||
break;
|
||||
case R.id.sb_min_scale:
|
||||
final float scale = 0.5f + (progress / 200f);
|
||||
scaleLayoutManager.setMinScale(scale);
|
||||
minScaleValue.setText(Util.formatFloat(scale));
|
||||
break;
|
||||
case R.id.sb_speed:
|
||||
final float speed = progress * 0.05f;
|
||||
scaleLayoutManager.setMoveSpeed(speed);
|
||||
speedValue.setText(Util.formatFloat(speed));
|
||||
break;
|
||||
case R.id.sb_max_alpha:
|
||||
final float maxAlpha = progress / 100f;
|
||||
scaleLayoutManager.setMaxAlpha(maxAlpha);
|
||||
maxAlphaValue.setText(Util.formatFloat(maxAlpha));
|
||||
break;
|
||||
case R.id.sb_min_alpha:
|
||||
final float minAlpha = progress / 100f;
|
||||
scaleLayoutManager.setMinAlpha(minAlpha);
|
||||
minAlphaValue.setText(Util.formatFloat(minAlpha));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
switch (buttonView.getId()) {
|
||||
case R.id.s_infinite:
|
||||
recyclerView.scrollToPosition(0);
|
||||
scaleLayoutManager.setInfinite(isChecked);
|
||||
break;
|
||||
case R.id.s_change_orientation:
|
||||
scaleLayoutManager.scrollToPosition(0);
|
||||
scaleLayoutManager.setOrientation(isChecked ?
|
||||
RecyclerView.VERTICAL : RecyclerView.HORIZONTAL);
|
||||
break;
|
||||
case R.id.s_auto_center:
|
||||
if (isChecked) {
|
||||
centerSnapHelper.attachToRecyclerView(recyclerView);
|
||||
} else {
|
||||
centerSnapHelper.attachToRecyclerView(null);
|
||||
}
|
||||
break;
|
||||
case R.id.s_reverse:
|
||||
scaleLayoutManager.scrollToPosition(0);
|
||||
scaleLayoutManager.setReverseLayout(isChecked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 168 KiB |
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 282 KiB |
After Width: | Height: | Size: 225 KiB |
After Width: | Height: | Size: 125 KiB |
After Width: | Height: | Size: 240 KiB |
After Width: | Height: | Size: 156 KiB |
After Width: | Height: | Size: 179 KiB |
After Width: | Height: | Size: 168 KiB |
After Width: | Height: | Size: 204 KiB |
After Width: | Height: | Size: 40 KiB |
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
tools:ignore="InvalidVectorPath">
|
||||
|
||||
<path android:pathData="M0 0h24v24H0z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M19.43 12.98c.04-.32 .07 -.64 .07 -.98s-.03-.66-.07-.98l2.11-1.65c.19-.15 .24
|
||||
-.42 .12 -.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49
|
||||
1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46 .18
|
||||
-.49 .42 l-.38 2.65c-.61 .25 -1.17 .59 -1.69 .98 l-2.49-1c-.23-.09-.49 0-.61 .22
|
||||
l-2 3.46c-.13 .22 -.07 .49 .12 .64 l2.11 1.65c-.04 .32 -.07 .65 -.07 .98 s.03
|
||||
.66 .07 .98 l-2.11 1.65c-.19 .15 -.24 .42 -.12 .64 l2 3.46c.12 .22 .39 .3 .61
|
||||
.22 l2.49-1c.52 .4 1.08 .73 1.69 .98 l.38 2.65c.03 .24 .24 .42 .49 .42 h4c.25 0
|
||||
.46-.18 .49 -.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23 .09 .49 0
|
||||
.61-.22l2-3.46c.12-.22 .07 -.49-.12-.64l-2.11-1.65zM12 15.5c-1.93
|
||||
0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z" />
|
||||
</vector>
|
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge 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"
|
||||
tools:context="com.common.commonlibtest.viewpagerlayoutmanager.VariousRvDemoActivity">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="horizontal" />
|
||||
|
||||
</merge>
|
@ -0,0 +1,154 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#66000000"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingBottom="20dp">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/item_space"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_space"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_item_space"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/min_scale"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/min_scale_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_min_scale"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/move_speed"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/speed_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_speed"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_reverse"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_reverse"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_infinite"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_infinite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/change_orientation"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_change_orientation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_auto_center"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_auto_center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
@ -0,0 +1,270 @@
|
||||
<?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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#66000000"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingBottom="20dp">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/radius"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/radius_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_radius"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/angle_interval"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interval_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_interval"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/center_scale"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/center_scale_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_center_scale"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/move_speed"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/speed_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_speed"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_reverse"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_reverse"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_infinite"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_infinite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_auto_center"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_auto_center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/flip_rotate"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_flip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/gravity"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_gravity"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/rb_left"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/left"
|
||||
android:textColor="#fff"
|
||||
app:buttonTint="@color/colorAccent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/rb_right"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/right"
|
||||
android:textColor="#fff"
|
||||
app:buttonTint="@color/colorAccent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/rb_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/top"
|
||||
android:textColor="#fff"
|
||||
app:buttonTint="@color/colorAccent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/rb_bottom"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/bottom"
|
||||
android:textColor="#fff"
|
||||
app:buttonTint="@color/colorAccent" />
|
||||
</RadioGroup>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/z_alignment"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_z_alignment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/rb_left_on_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/left_on_top"
|
||||
android:textColor="#fff"
|
||||
app:buttonTint="@color/colorAccent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/rb_right_on_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/right_on_top"
|
||||
android:textColor="#fff"
|
||||
app:buttonTint="@color/colorAccent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/rb_center_on_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/center_on_top"
|
||||
android:textColor="#fff"
|
||||
app:buttonTint="@color/colorAccent" />
|
||||
</RadioGroup>
|
||||
</LinearLayout>
|
@ -0,0 +1,270 @@
|
||||
<?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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#66000000"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingBottom="20dp">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/radius"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/radius_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_radius"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/angle_interval"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interval_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_interval"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/move_speed"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/speed_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_speed"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/distance_to_bottom"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/distance_to_bottom_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_distance_to_bottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_reverse"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_reverse"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_infinite"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_infinite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_auto_center"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_auto_center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/flip_rotate"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_flip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/gravity"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_gravity"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/rb_left"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/left"
|
||||
android:textColor="#fff"
|
||||
app:buttonTint="@color/colorAccent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/rb_right"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/right"
|
||||
android:textColor="#fff"
|
||||
app:buttonTint="@color/colorAccent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/rb_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/top"
|
||||
android:textColor="#fff"
|
||||
app:buttonTint="@color/colorAccent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/rb_bottom"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/bottom"
|
||||
android:textColor="#fff"
|
||||
app:buttonTint="@color/colorAccent" />
|
||||
</RadioGroup>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/z_alignment"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_z_alignment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/rb_left_on_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/left_on_top"
|
||||
android:textColor="#fff"
|
||||
app:buttonTint="@color/colorAccent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/rb_right_on_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/right_on_top"
|
||||
android:textColor="#fff"
|
||||
app:buttonTint="@color/colorAccent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatRadioButton
|
||||
android:id="@+id/rb_center_on_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/center_on_top"
|
||||
android:textColor="#fff"
|
||||
app:buttonTint="@color/colorAccent" />
|
||||
</RadioGroup>
|
||||
</LinearLayout>
|
@ -0,0 +1,261 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#66000000"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingBottom="20dp">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/item_space"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_space"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_item_space"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/angle_interval"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/angle_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_interval"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/max_alpha"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/max_alpha_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_max_alpha"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/min_alpha"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/min_alpha_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_min_alpha"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/move_speed"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/speed_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_speed"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_reverse"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_reverse"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_infinite"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_infinite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/change_orientation"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_change_orientation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_auto_center"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_auto_center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_bring_center_in_front"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_center_in_front"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/flip_rotate"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_flip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/rotate_from_edge"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_rotate_from_edge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
@ -0,0 +1,174 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#66000000"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingBottom="20dp">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/item_space"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_space"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_item_space"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/rotate_angle"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/angle_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_angle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/move_speed"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/speed_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_speed"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_reverse"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_reverse"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_infinite"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_infinite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/change_orientation"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_change_orientation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_reverse_rotate"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
|
||||
android:id="@+id/s_reverse_rotate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_auto_center"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
|
||||
android:id="@+id/s_auto_center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
@ -0,0 +1,202 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#66000000"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="20dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingTop="20dp">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/item_space"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_space"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_item_space"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/min_scale"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/min_scale_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_min_scale"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/move_speed"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/speed_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_speed"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/max_alpha"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/max_alpha_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_max_alpha"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/min_alpha"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/min_alpha_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="#fff" />
|
||||
</FrameLayout>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_min_alpha"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_reverse"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_reverse"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_infinite"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_infinite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/change_orientation"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_change_orientation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/toggle_auto_center"
|
||||
android:textColor="#fff" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/s_auto_center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end" />
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="300dp"
|
||||
app:cardCornerRadius="5dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop" />
|
||||
</androidx.cardview.widget.CardView>
|
@ -0,0 +1,59 @@
|
||||
<?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"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
tools:context="com.common.commonlibtest.viewpagerlayoutmanager.VariousRvDemoActivity">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/bt_circle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/circle_layout_manger"
|
||||
app:textAllCaps="false" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/bt_scale"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/scale_layout_manager"
|
||||
app:textAllCaps="false" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/bt_circle_scale"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/circle_scale_layout_manager"
|
||||
app:textAllCaps="false" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/bt_elevate_scale"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/carousel_layout_manager"
|
||||
app:textAllCaps="false" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/bt_gallery"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/gallery_layout_manager"
|
||||
app:textAllCaps="false" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/bt_rotate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/rotate_layout_manager"
|
||||
app:textAllCaps="false" />
|
||||
</LinearLayout>
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item android:id="@+id/setting"
|
||||
android:title="@string/setting"
|
||||
app:showAsAction="always"/>
|
||||
</menu>
|
@ -1,3 +1,39 @@
|
||||
<resources>
|
||||
<string name="app_name">CommonLibTest</string>
|
||||
|
||||
<string name="setting">Setting</string>
|
||||
<string name="circle_layout_manger">Circle LayoutManager</string>
|
||||
<string name="circle_scale_layout_manager">Circle Scale LayoutManager</string>
|
||||
<string name="carousel_layout_manager">Carousel LayoutManager</string>
|
||||
<string name="gallery_layout_manager">Gallery LayoutManager</string>
|
||||
<string name="rotate_layout_manager">Rotate LayoutManager</string>
|
||||
<string name="scale_layout_manager">Scale LayoutManager</string>
|
||||
|
||||
<string name="radius">Radius</string>
|
||||
<string name="item_space">Item Space</string>
|
||||
<string name="move_speed">Move Speed</string>
|
||||
<string name="distance_to_bottom">Distance To Bottom</string>
|
||||
<string name="angle_interval">Interval Angle</string>
|
||||
<string name="center_scale">Center Scale</string>
|
||||
<string name="rotate_angle">Rotate Angle</string>
|
||||
<string name="min_scale">Min Scale</string>
|
||||
<string name="max_alpha">Max Alpha</string>
|
||||
<string name="min_alpha">Min Alpha</string>
|
||||
<string name="toggle_infinite">Toggle Infinite</string>
|
||||
<string name="toggle_bring_center_in_front">Toggle Bring Center In Front</string>
|
||||
<string name="toggle_reverse_rotate">Toggle Reverse Rotate</string>
|
||||
<string name="toggle_auto_center">Toggle Auto Center</string>
|
||||
<string name="toggle_reverse">Toggle Reverse</string>
|
||||
<string name="flip_rotate">Flip Rotate</string>
|
||||
<string name="rotate_from_edge">Rotate From Edge</string>
|
||||
<string name="change_orientation">Change Orientation</string>
|
||||
<string name="gravity">Gravity</string>
|
||||
<string name="left">Left</string>
|
||||
<string name="right">Right</string>
|
||||
<string name="top">Top</string>
|
||||
<string name="bottom">Bottom</string>
|
||||
<string name="z_alignment">Z-Alignment</string>
|
||||
<string name="left_on_top">Left On Top</string>
|
||||
<string name="right_on_top">Right On Top</string>
|
||||
<string name="center_on_top">Center On Top</string>
|
||||
</resources>
|
@ -0,0 +1,63 @@
|
||||
package com.common.commonlib.view.viewpagerlayoutmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.common.commonlib.R;
|
||||
|
||||
public class AutoPlayRecyclerView extends RecyclerView {
|
||||
private AutoPlaySnapHelper autoPlaySnapHelper;
|
||||
|
||||
public AutoPlayRecyclerView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public AutoPlayRecyclerView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public AutoPlayRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AutoPlayRecyclerView);
|
||||
final int timeInterval = typedArray.getInt(R.styleable.AutoPlayRecyclerView_timeInterval, AutoPlaySnapHelper.TIME_INTERVAL);
|
||||
final int direction = typedArray.getInt(R.styleable.AutoPlayRecyclerView_direction, AutoPlaySnapHelper.RIGHT);
|
||||
typedArray.recycle();
|
||||
autoPlaySnapHelper = new AutoPlaySnapHelper(timeInterval, direction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
boolean result = super.dispatchTouchEvent(ev);
|
||||
switch (ev.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
if (autoPlaySnapHelper != null) {
|
||||
autoPlaySnapHelper.pause();
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (autoPlaySnapHelper != null) {
|
||||
autoPlaySnapHelper.start();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
autoPlaySnapHelper.start();
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
autoPlaySnapHelper.pause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLayoutManager(LayoutManager layout) {
|
||||
super.setLayoutManager(layout);
|
||||
autoPlaySnapHelper.attachToRecyclerView(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package com.common.commonlib.view.viewpagerlayoutmanager;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.Scroller;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
|
||||
/**
|
||||
* Used by {@link AutoPlayRecyclerView} to implement auto play effect
|
||||
*/
|
||||
|
||||
class AutoPlaySnapHelper extends CenterSnapHelper {
|
||||
final static int TIME_INTERVAL = 2000;
|
||||
|
||||
final static int LEFT = 1;
|
||||
final static int RIGHT = 2;
|
||||
|
||||
private Handler handler;
|
||||
private int timeInterval;
|
||||
private Runnable autoPlayRunnable;
|
||||
private boolean runnableAdded;
|
||||
private int direction;
|
||||
|
||||
AutoPlaySnapHelper(int timeInterval, int direction) {
|
||||
checkTimeInterval(timeInterval);
|
||||
checkDirection(direction);
|
||||
handler = new Handler(Looper.getMainLooper());
|
||||
this.timeInterval = timeInterval;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws IllegalStateException {
|
||||
if (mRecyclerView == recyclerView) {
|
||||
return; // nothing to do
|
||||
}
|
||||
if (mRecyclerView != null) {
|
||||
destroyCallbacks();
|
||||
}
|
||||
mRecyclerView = recyclerView;
|
||||
if (mRecyclerView != null) {
|
||||
final RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
|
||||
if (!(layoutManager instanceof ViewPagerLayoutManager)) return;
|
||||
|
||||
setupCallbacks();
|
||||
mGravityScroller = new Scroller(mRecyclerView.getContext(),
|
||||
new DecelerateInterpolator());
|
||||
|
||||
snapToCenterView((ViewPagerLayoutManager) layoutManager,
|
||||
((ViewPagerLayoutManager) layoutManager).onPageChangeListener);
|
||||
|
||||
((ViewPagerLayoutManager) layoutManager).setInfinite(true);
|
||||
|
||||
autoPlayRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final int currentPosition =
|
||||
((ViewPagerLayoutManager) layoutManager).getCurrentPositionOffset() *
|
||||
(((ViewPagerLayoutManager) layoutManager).getReverseLayout() ? -1 : 1);
|
||||
ScrollHelper.smoothScrollToPosition(mRecyclerView,
|
||||
(ViewPagerLayoutManager) layoutManager, direction == RIGHT ? currentPosition + 1 : currentPosition - 1);
|
||||
handler.postDelayed(autoPlayRunnable, timeInterval);
|
||||
}
|
||||
};
|
||||
handler.postDelayed(autoPlayRunnable, timeInterval);
|
||||
runnableAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void destroyCallbacks() {
|
||||
super.destroyCallbacks();
|
||||
if (runnableAdded) {
|
||||
handler.removeCallbacks(autoPlayRunnable);
|
||||
runnableAdded = false;
|
||||
}
|
||||
}
|
||||
|
||||
void pause() {
|
||||
if (runnableAdded) {
|
||||
handler.removeCallbacks(autoPlayRunnable);
|
||||
runnableAdded = false;
|
||||
}
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (!runnableAdded) {
|
||||
handler.postDelayed(autoPlayRunnable, timeInterval);
|
||||
runnableAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
void setTimeInterval(int timeInterval) {
|
||||
checkTimeInterval(timeInterval);
|
||||
this.timeInterval = timeInterval;
|
||||
}
|
||||
|
||||
void setDirection(int direction) {
|
||||
checkDirection(direction);
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
private void checkDirection(int direction) {
|
||||
if (direction != LEFT && direction != RIGHT)
|
||||
throw new IllegalArgumentException("direction should be one of left or right");
|
||||
}
|
||||
|
||||
private void checkTimeInterval(int timeInterval) {
|
||||
if (timeInterval <= 0)
|
||||
throw new IllegalArgumentException("time interval should greater than 0");
|
||||
}
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
package com.common.commonlib.view.viewpagerlayoutmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ViewPagerLayoutManager}
|
||||
* which layouts items like carousel
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class CarouselLayoutManager extends ViewPagerLayoutManager {
|
||||
|
||||
private int itemSpace;
|
||||
private float minScale;
|
||||
private float moveSpeed;
|
||||
|
||||
public CarouselLayoutManager(Context context, int itemSpace) {
|
||||
this(new Builder(context, itemSpace));
|
||||
}
|
||||
|
||||
public CarouselLayoutManager(Context context, int itemSpace, int orientation) {
|
||||
this(new Builder(context, itemSpace).setOrientation(orientation));
|
||||
}
|
||||
|
||||
public CarouselLayoutManager(Context context, int itemSpace, int orientation, boolean reverseLayout) {
|
||||
this(new Builder(context, itemSpace).setOrientation(orientation).setReverseLayout(reverseLayout));
|
||||
}
|
||||
|
||||
public CarouselLayoutManager(Builder builder) {
|
||||
this(builder.context, builder.itemSpace, builder.minScale, builder.orientation,
|
||||
builder.maxVisibleItemCount, builder.moveSpeed, builder.distanceToBottom,
|
||||
builder.reverseLayout);
|
||||
}
|
||||
|
||||
private CarouselLayoutManager(Context context, int itemSpace, float minScale, int orientation,
|
||||
int maxVisibleItemCount, float moveSpeed, int distanceToBottom,
|
||||
boolean reverseLayout) {
|
||||
super(context, orientation, reverseLayout);
|
||||
setEnableBringCenterToFront(true);
|
||||
setDistanceToBottom(distanceToBottom);
|
||||
setMaxVisibleItemCount(maxVisibleItemCount);
|
||||
this.itemSpace = itemSpace;
|
||||
this.minScale = minScale;
|
||||
this.moveSpeed = moveSpeed;
|
||||
}
|
||||
|
||||
public int getItemSpace() {
|
||||
return itemSpace;
|
||||
}
|
||||
|
||||
public float getMinScale() {
|
||||
return minScale;
|
||||
}
|
||||
|
||||
public float getMoveSpeed() {
|
||||
return moveSpeed;
|
||||
}
|
||||
|
||||
public void setItemSpace(int itemSpace) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.itemSpace == itemSpace) return;
|
||||
this.itemSpace = itemSpace;
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
public void setMinScale(float minScale) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (minScale > 1f) minScale = 1f;
|
||||
if (this.minScale == minScale) return;
|
||||
this.minScale = minScale;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void setMoveSpeed(float moveSpeed) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.moveSpeed == moveSpeed) return;
|
||||
this.moveSpeed = moveSpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float setInterval() {
|
||||
return (mDecoratedMeasurement - itemSpace);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setItemViewProperty(View itemView, float targetOffset) {
|
||||
float scale = calculateScale(targetOffset + mSpaceMain);
|
||||
itemView.setScaleX(scale);
|
||||
itemView.setScaleY(scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getDistanceRatio() {
|
||||
if (moveSpeed == 0) return Float.MAX_VALUE;
|
||||
return 1 / moveSpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float setViewElevation(View itemView, float targetOffset) {
|
||||
return itemView.getScaleX() * 5;
|
||||
}
|
||||
|
||||
private float calculateScale(float x) {
|
||||
float deltaX = Math.abs(x - (mOrientationHelper.getTotalSpace() - mDecoratedMeasurement) / 2f);
|
||||
return (minScale - 1) * deltaX / (mOrientationHelper.getTotalSpace() / 2f) + 1f;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private static final float DEFAULT_SPEED = 1f;
|
||||
private static final float MIN_SCALE = 0.5f;
|
||||
|
||||
private Context context;
|
||||
private int itemSpace;
|
||||
private int orientation;
|
||||
private float minScale;
|
||||
private float moveSpeed;
|
||||
private int maxVisibleItemCount;
|
||||
private boolean reverseLayout;
|
||||
private int distanceToBottom;
|
||||
|
||||
public Builder(Context context, int itemSpace) {
|
||||
this.itemSpace = itemSpace;
|
||||
this.context = context;
|
||||
orientation = HORIZONTAL;
|
||||
minScale = MIN_SCALE;
|
||||
this.moveSpeed = DEFAULT_SPEED;
|
||||
reverseLayout = false;
|
||||
maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN;
|
||||
distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE;
|
||||
}
|
||||
|
||||
public Builder setOrientation(int orientation) {
|
||||
this.orientation = orientation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMinScale(float minScale) {
|
||||
this.minScale = minScale;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setReverseLayout(boolean reverseLayout) {
|
||||
this.reverseLayout = reverseLayout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMoveSpeed(float moveSpeed) {
|
||||
this.moveSpeed = moveSpeed;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMaxVisibleItemCount(int maxVisibleItemCount) {
|
||||
this.maxVisibleItemCount = maxVisibleItemCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDistanceToBottom(int distanceToBottom) {
|
||||
this.distanceToBottom = distanceToBottom;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CarouselLayoutManager build() {
|
||||
return new CarouselLayoutManager(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
package com.common.commonlib.view.viewpagerlayoutmanager;
|
||||
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.Scroller;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class CenterSnapHelper extends RecyclerView.OnFlingListener {
|
||||
|
||||
RecyclerView mRecyclerView;
|
||||
Scroller mGravityScroller;
|
||||
|
||||
/**
|
||||
* when the dataSet is extremely large
|
||||
* {@link #snapToCenterView(ViewPagerLayoutManager, ViewPagerLayoutManager.OnPageChangeListener)}
|
||||
* may keep calling itself because the accuracy of float
|
||||
*/
|
||||
private boolean snapToCenter = false;
|
||||
|
||||
// Handles the snap on scroll case.
|
||||
private final RecyclerView.OnScrollListener mScrollListener =
|
||||
new RecyclerView.OnScrollListener() {
|
||||
|
||||
boolean mScrolled = false;
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
|
||||
super.onScrollStateChanged(recyclerView, newState);
|
||||
|
||||
final ViewPagerLayoutManager layoutManager =
|
||||
(ViewPagerLayoutManager) recyclerView.getLayoutManager();
|
||||
final ViewPagerLayoutManager.OnPageChangeListener onPageChangeListener =
|
||||
layoutManager.onPageChangeListener;
|
||||
if (onPageChangeListener != null) {
|
||||
onPageChangeListener.onPageScrollStateChanged(newState);
|
||||
}
|
||||
|
||||
if (newState == RecyclerView.SCROLL_STATE_IDLE && mScrolled) {
|
||||
mScrolled = false;
|
||||
if (!snapToCenter) {
|
||||
snapToCenter = true;
|
||||
snapToCenterView(layoutManager, onPageChangeListener);
|
||||
} else {
|
||||
snapToCenter = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
if (dx != 0 || dy != 0) {
|
||||
mScrolled = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean onFling(int velocityX, int velocityY) {
|
||||
ViewPagerLayoutManager layoutManager = (ViewPagerLayoutManager) mRecyclerView.getLayoutManager();
|
||||
if (layoutManager == null) {
|
||||
return false;
|
||||
}
|
||||
RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
|
||||
if (adapter == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!layoutManager.getInfinite() &&
|
||||
(layoutManager.mOffset == layoutManager.getMaxOffset()
|
||||
|| layoutManager.mOffset == layoutManager.getMinOffset())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int minFlingVelocity = mRecyclerView.getMinFlingVelocity();
|
||||
mGravityScroller.fling(0, 0, velocityX, velocityY,
|
||||
Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
|
||||
|
||||
if (layoutManager.mOrientation == ViewPagerLayoutManager.VERTICAL
|
||||
&& Math.abs(velocityY) > minFlingVelocity) {
|
||||
final int currentPosition = layoutManager.getCurrentPositionOffset();
|
||||
final int offsetPosition = (int) (mGravityScroller.getFinalY() /
|
||||
layoutManager.mInterval / layoutManager.getDistanceRatio());
|
||||
ScrollHelper.smoothScrollToPosition(mRecyclerView, layoutManager, layoutManager.getReverseLayout() ?
|
||||
-currentPosition - offsetPosition : currentPosition + offsetPosition);
|
||||
return true;
|
||||
} else if (layoutManager.mOrientation == ViewPagerLayoutManager.HORIZONTAL
|
||||
&& Math.abs(velocityX) > minFlingVelocity) {
|
||||
final int currentPosition = layoutManager.getCurrentPositionOffset();
|
||||
final int offsetPosition = (int) (mGravityScroller.getFinalX() /
|
||||
layoutManager.mInterval / layoutManager.getDistanceRatio());
|
||||
ScrollHelper.smoothScrollToPosition(mRecyclerView, layoutManager, layoutManager.getReverseLayout() ?
|
||||
-currentPosition - offsetPosition : currentPosition + offsetPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void attachToRecyclerView(@Nullable RecyclerView recyclerView)
|
||||
throws IllegalStateException {
|
||||
if (mRecyclerView == recyclerView) {
|
||||
return; // nothing to do
|
||||
}
|
||||
if (mRecyclerView != null) {
|
||||
destroyCallbacks();
|
||||
}
|
||||
mRecyclerView = recyclerView;
|
||||
if (mRecyclerView != null) {
|
||||
final RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
|
||||
if (!(layoutManager instanceof ViewPagerLayoutManager)) return;
|
||||
|
||||
setupCallbacks();
|
||||
mGravityScroller = new Scroller(mRecyclerView.getContext(),
|
||||
new DecelerateInterpolator());
|
||||
|
||||
snapToCenterView((ViewPagerLayoutManager) layoutManager,
|
||||
((ViewPagerLayoutManager) layoutManager).onPageChangeListener);
|
||||
}
|
||||
}
|
||||
|
||||
void snapToCenterView(ViewPagerLayoutManager layoutManager,
|
||||
ViewPagerLayoutManager.OnPageChangeListener listener) {
|
||||
final int delta = layoutManager.getOffsetToCenter();
|
||||
if (delta != 0) {
|
||||
if (layoutManager.getOrientation()
|
||||
== RecyclerView.VERTICAL)
|
||||
mRecyclerView.smoothScrollBy(0, delta);
|
||||
else
|
||||
mRecyclerView.smoothScrollBy(delta, 0);
|
||||
} else {
|
||||
// set it false to make smoothScrollToPosition keep trigger the listener
|
||||
snapToCenter = false;
|
||||
}
|
||||
|
||||
if (listener != null)
|
||||
listener.onPageSelected(layoutManager.getCurrentPosition());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an instance of a {@link RecyclerView} is attached.
|
||||
*/
|
||||
void setupCallbacks() throws IllegalStateException {
|
||||
if (mRecyclerView.getOnFlingListener() != null) {
|
||||
throw new IllegalStateException("An instance of OnFlingListener already set.");
|
||||
}
|
||||
mRecyclerView.addOnScrollListener(mScrollListener);
|
||||
mRecyclerView.setOnFlingListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the instance of a {@link RecyclerView} is detached.
|
||||
*/
|
||||
void destroyCallbacks() {
|
||||
mRecyclerView.removeOnScrollListener(mScrollListener);
|
||||
mRecyclerView.setOnFlingListener(null);
|
||||
}
|
||||
}
|
@ -0,0 +1,357 @@
|
||||
package com.common.commonlib.view.viewpagerlayoutmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ViewPagerLayoutManager}
|
||||
* which layouts item in a circle
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class CircleLayoutManager extends ViewPagerLayoutManager {
|
||||
public static final int LEFT = 10;
|
||||
public static final int RIGHT = 11;
|
||||
public static final int TOP = 12;
|
||||
public static final int BOTTOM = 13;
|
||||
|
||||
public static final int LEFT_ON_TOP = 4;
|
||||
public static final int RIGHT_ON_TOP = 5;
|
||||
public static final int CENTER_ON_TOP = 6;
|
||||
|
||||
private int radius;
|
||||
private int angleInterval;
|
||||
private float moveSpeed;
|
||||
private float maxRemoveAngle;
|
||||
private float minRemoveAngle;
|
||||
private int gravity;
|
||||
private boolean flipRotate;
|
||||
private int zAlignment;
|
||||
|
||||
public CircleLayoutManager(Context context) {
|
||||
this(new Builder(context));
|
||||
}
|
||||
|
||||
public CircleLayoutManager(Context context, boolean reverseLayout) {
|
||||
this(new Builder(context).setReverseLayout(reverseLayout));
|
||||
}
|
||||
|
||||
public CircleLayoutManager(Context context, int gravity, boolean reverseLayout) {
|
||||
this(new Builder(context).setGravity(gravity).setReverseLayout(reverseLayout));
|
||||
}
|
||||
|
||||
public CircleLayoutManager(Builder builder) {
|
||||
this(builder.context, builder.radius, builder.angleInterval, builder.moveSpeed, builder.maxRemoveAngle,
|
||||
builder.minRemoveAngle, builder.gravity, builder.zAlignment, builder.flipRotate,
|
||||
builder.maxVisibleItemCount, builder.distanceToBottom, builder.reverseLayout);
|
||||
}
|
||||
|
||||
private CircleLayoutManager(Context context, int radius, int angleInterval, float moveSpeed,
|
||||
float max, float min, int gravity, int zAlignment, boolean flipRotate,
|
||||
int maxVisibleItemCount, int distanceToBottom, boolean reverseLayout) {
|
||||
super(context, (gravity == LEFT || gravity == RIGHT) ? VERTICAL : HORIZONTAL, reverseLayout);
|
||||
setEnableBringCenterToFront(true);
|
||||
setMaxVisibleItemCount(maxVisibleItemCount);
|
||||
setDistanceToBottom(distanceToBottom);
|
||||
this.radius = radius;
|
||||
this.angleInterval = angleInterval;
|
||||
this.moveSpeed = moveSpeed;
|
||||
this.maxRemoveAngle = max;
|
||||
this.minRemoveAngle = min;
|
||||
this.gravity = gravity;
|
||||
this.flipRotate = flipRotate;
|
||||
this.zAlignment = zAlignment;
|
||||
}
|
||||
|
||||
private static void assertGravity(int gravity) {
|
||||
if (gravity != LEFT && gravity != RIGHT && gravity != TOP && gravity != BOTTOM) {
|
||||
throw new IllegalArgumentException("gravity must be one of LEFT RIGHT TOP and BOTTOM");
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertZAlignmentState(int zAlignment) {
|
||||
if (zAlignment != LEFT_ON_TOP && zAlignment != RIGHT_ON_TOP && zAlignment != CENTER_ON_TOP) {
|
||||
throw new IllegalArgumentException("zAlignment must be one of LEFT_ON_TOP RIGHT_ON_TOP and CENTER_ON_TOP");
|
||||
}
|
||||
}
|
||||
|
||||
public int getRadius() {
|
||||
return radius;
|
||||
}
|
||||
|
||||
public void setRadius(int radius) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.radius == radius) return;
|
||||
this.radius = radius;
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
public int getAngleInterval() {
|
||||
return angleInterval;
|
||||
}
|
||||
|
||||
public void setAngleInterval(int angleInterval) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.angleInterval == angleInterval) return;
|
||||
this.angleInterval = angleInterval;
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
public float getMoveSpeed() {
|
||||
return moveSpeed;
|
||||
}
|
||||
|
||||
public void setMoveSpeed(float moveSpeed) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.moveSpeed == moveSpeed) return;
|
||||
this.moveSpeed = moveSpeed;
|
||||
}
|
||||
|
||||
public float getMaxRemoveAngle() {
|
||||
return maxRemoveAngle;
|
||||
}
|
||||
|
||||
public void setMaxRemoveAngle(float maxRemoveAngle) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.maxRemoveAngle == maxRemoveAngle) return;
|
||||
this.maxRemoveAngle = maxRemoveAngle;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public float getMinRemoveAngle() {
|
||||
return minRemoveAngle;
|
||||
}
|
||||
|
||||
public void setMinRemoveAngle(float minRemoveAngle) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.minRemoveAngle == minRemoveAngle) return;
|
||||
this.minRemoveAngle = minRemoveAngle;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public int getGravity() {
|
||||
return gravity;
|
||||
}
|
||||
|
||||
public void setGravity(int gravity) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
assertGravity(gravity);
|
||||
if (this.gravity == gravity) return;
|
||||
this.gravity = gravity;
|
||||
if (gravity == LEFT || gravity == RIGHT) {
|
||||
setOrientation(RecyclerView.VERTICAL);
|
||||
} else {
|
||||
setOrientation(RecyclerView.HORIZONTAL);
|
||||
}
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public boolean getFlipRotate() {
|
||||
return flipRotate;
|
||||
}
|
||||
|
||||
public void setFlipRotate(boolean flipRotate) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.flipRotate == flipRotate) return;
|
||||
this.flipRotate = flipRotate;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public int getZAlignment() {
|
||||
return zAlignment;
|
||||
}
|
||||
|
||||
public void setZAlignment(int zAlignment) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
assertZAlignmentState(zAlignment);
|
||||
if (this.zAlignment == zAlignment) return;
|
||||
this.zAlignment = zAlignment;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float setInterval() {
|
||||
return angleInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() {
|
||||
radius = radius == Builder.INVALID_VALUE ? mDecoratedMeasurementInOther : radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float maxRemoveOffset() {
|
||||
return maxRemoveAngle;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float minRemoveOffset() {
|
||||
return minRemoveAngle;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calItemLeft(View itemView, float targetOffset) {
|
||||
switch (gravity) {
|
||||
case LEFT:
|
||||
return (int) (radius * Math.sin(Math.toRadians(90 - targetOffset)) - radius);
|
||||
case RIGHT:
|
||||
return (int) (radius - radius * Math.sin(Math.toRadians(90 - targetOffset)));
|
||||
case TOP:
|
||||
case BOTTOM:
|
||||
default:
|
||||
return (int) (radius * Math.cos(Math.toRadians(90 - targetOffset)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calItemTop(View itemView, float targetOffset) {
|
||||
switch (gravity) {
|
||||
case LEFT:
|
||||
case RIGHT:
|
||||
return (int) (radius * Math.cos(Math.toRadians(90 - targetOffset)));
|
||||
case TOP:
|
||||
return (int) (radius * Math.sin(Math.toRadians(90 - targetOffset)) - radius);
|
||||
case BOTTOM:
|
||||
default:
|
||||
return (int) (radius - radius * Math.sin(Math.toRadians(90 - targetOffset)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setItemViewProperty(View itemView, float targetOffset) {
|
||||
switch (gravity) {
|
||||
case RIGHT:
|
||||
case TOP:
|
||||
if (flipRotate) {
|
||||
itemView.setRotation(targetOffset);
|
||||
} else {
|
||||
itemView.setRotation(360 - targetOffset);
|
||||
}
|
||||
break;
|
||||
case LEFT:
|
||||
case BOTTOM:
|
||||
default:
|
||||
if (flipRotate) {
|
||||
itemView.setRotation(360 - targetOffset);
|
||||
} else {
|
||||
itemView.setRotation(targetOffset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float setViewElevation(View itemView, float targetOffset) {
|
||||
if (zAlignment == LEFT_ON_TOP)
|
||||
return (540 - targetOffset) / 72;
|
||||
else if (zAlignment == RIGHT_ON_TOP)
|
||||
return (targetOffset - 540) / 72;
|
||||
else
|
||||
return (360 - Math.abs(targetOffset)) / 72;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getDistanceRatio() {
|
||||
if (moveSpeed == 0) return Float.MAX_VALUE;
|
||||
return 1 / moveSpeed;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private static int INTERVAL_ANGLE = 30;// The default mInterval angle between each items
|
||||
private static float DISTANCE_RATIO = 10f; // Finger swipe distance divide item rotate angle
|
||||
private static int INVALID_VALUE = Integer.MIN_VALUE;
|
||||
private static int MAX_REMOVE_ANGLE = 90;
|
||||
private static int MIN_REMOVE_ANGLE = -90;
|
||||
|
||||
private int radius;
|
||||
private int angleInterval;
|
||||
private float moveSpeed;
|
||||
private float maxRemoveAngle;
|
||||
private float minRemoveAngle;
|
||||
private boolean reverseLayout;
|
||||
private Context context;
|
||||
private int gravity;
|
||||
private boolean flipRotate;
|
||||
private int zAlignment;
|
||||
private int maxVisibleItemCount;
|
||||
private int distanceToBottom;
|
||||
|
||||
public Builder(Context context) {
|
||||
this.context = context;
|
||||
radius = INVALID_VALUE;
|
||||
angleInterval = INTERVAL_ANGLE;
|
||||
moveSpeed = 1 / DISTANCE_RATIO;
|
||||
maxRemoveAngle = MAX_REMOVE_ANGLE;
|
||||
minRemoveAngle = MIN_REMOVE_ANGLE;
|
||||
reverseLayout = false;
|
||||
flipRotate = false;
|
||||
gravity = BOTTOM;
|
||||
zAlignment = LEFT_ON_TOP;
|
||||
maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN;
|
||||
distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE;
|
||||
}
|
||||
|
||||
public Builder setRadius(int radius) {
|
||||
this.radius = radius;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAngleInterval(int angleInterval) {
|
||||
this.angleInterval = angleInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMoveSpeed(int moveSpeed) {
|
||||
this.moveSpeed = moveSpeed;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMaxRemoveAngle(float maxRemoveAngle) {
|
||||
this.maxRemoveAngle = maxRemoveAngle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMinRemoveAngle(float minRemoveAngle) {
|
||||
this.minRemoveAngle = minRemoveAngle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setReverseLayout(boolean reverseLayout) {
|
||||
this.reverseLayout = reverseLayout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setGravity(int gravity) {
|
||||
assertGravity(gravity);
|
||||
this.gravity = gravity;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFlipRotate(boolean flipRotate) {
|
||||
this.flipRotate = flipRotate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setZAlignment(int zAlignment) {
|
||||
assertZAlignmentState(zAlignment);
|
||||
this.zAlignment = zAlignment;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMaxVisibleItemCount(int maxVisibleItemCount) {
|
||||
this.maxVisibleItemCount = maxVisibleItemCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDistanceToBottom(int distanceToBottom) {
|
||||
this.distanceToBottom = distanceToBottom;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CircleLayoutManager build() {
|
||||
return new CircleLayoutManager(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,395 @@
|
||||
package com.common.commonlib.view.viewpagerlayoutmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ViewPagerLayoutManager}
|
||||
* which layouts item in a circle and will change the child's centerScale while scrolling
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class CircleScaleLayoutManager extends ViewPagerLayoutManager {
|
||||
public static final int LEFT = 10;
|
||||
public static final int RIGHT = 11;
|
||||
public static final int TOP = 12;
|
||||
public static final int BOTTOM = 13;
|
||||
|
||||
public static final int LEFT_ON_TOP = 4;
|
||||
public static final int RIGHT_ON_TOP = 5;
|
||||
public static final int CENTER_ON_TOP = 6;
|
||||
|
||||
private int radius;
|
||||
private int angleInterval;
|
||||
private float moveSpeed;
|
||||
private float centerScale;
|
||||
private float maxRemoveAngle;
|
||||
private float minRemoveAngle;
|
||||
private int gravity;
|
||||
private boolean flipRotate;
|
||||
private int zAlignment;
|
||||
|
||||
public CircleScaleLayoutManager(Context context) {
|
||||
this(new Builder(context));
|
||||
}
|
||||
|
||||
public CircleScaleLayoutManager(Context context, int gravity, boolean reverseLayout) {
|
||||
this(new Builder(context).setGravity(gravity).setReverseLayout(reverseLayout));
|
||||
}
|
||||
|
||||
public CircleScaleLayoutManager(Context context, boolean reverseLayout) {
|
||||
this(new Builder(context).setReverseLayout(reverseLayout));
|
||||
}
|
||||
|
||||
public CircleScaleLayoutManager(Builder builder) {
|
||||
this(builder.context, builder.radius, builder.angleInterval, builder.centerScale, builder.moveSpeed,
|
||||
builder.maxRemoveAngle, builder.minRemoveAngle, builder.gravity, builder.zAlignment,
|
||||
builder.flipRotate, builder.maxVisibleItemCount, builder.distanceToBottom, builder.reverseLayout);
|
||||
}
|
||||
|
||||
private CircleScaleLayoutManager(Context context, int radius, int angleInterval, float centerScale,
|
||||
float moveSpeed, float max, float min, int gravity, int zAlignment,
|
||||
boolean flipRotate, int maxVisibleItemCount, int distanceToBottom, boolean reverseLayout) {
|
||||
super(context, HORIZONTAL, reverseLayout);
|
||||
setEnableBringCenterToFront(true);
|
||||
setMaxVisibleItemCount(maxVisibleItemCount);
|
||||
setDistanceToBottom(distanceToBottom);
|
||||
this.radius = radius;
|
||||
this.angleInterval = angleInterval;
|
||||
this.centerScale = centerScale;
|
||||
this.moveSpeed = moveSpeed;
|
||||
this.maxRemoveAngle = max;
|
||||
this.minRemoveAngle = min;
|
||||
this.gravity = gravity;
|
||||
this.flipRotate = flipRotate;
|
||||
this.zAlignment = zAlignment;
|
||||
}
|
||||
|
||||
private static void assertGravity(int gravity) {
|
||||
if (gravity != LEFT && gravity != RIGHT && gravity != TOP && gravity != BOTTOM) {
|
||||
throw new IllegalArgumentException("gravity must be one of LEFT RIGHT TOP and BOTTOM");
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertZAlignmentState(int zAlignment) {
|
||||
if (zAlignment != LEFT_ON_TOP && zAlignment != RIGHT_ON_TOP && zAlignment != CENTER_ON_TOP) {
|
||||
throw new IllegalArgumentException("zAlignment must be one of LEFT_ON_TOP RIGHT_ON_TOP and CENTER_ON_TOP");
|
||||
}
|
||||
}
|
||||
|
||||
public int getRadius() {
|
||||
return radius;
|
||||
}
|
||||
|
||||
public void setRadius(int radius) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.radius == radius) return;
|
||||
this.radius = radius;
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
public int getAngleInterval() {
|
||||
return angleInterval;
|
||||
}
|
||||
|
||||
public void setAngleInterval(int angleInterval) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.angleInterval == angleInterval) return;
|
||||
this.angleInterval = angleInterval;
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
public float getCenterScale() {
|
||||
return centerScale;
|
||||
}
|
||||
|
||||
public void setCenterScale(float centerScale) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.centerScale == centerScale) return;
|
||||
this.centerScale = centerScale;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public float getMoveSpeed() {
|
||||
return moveSpeed;
|
||||
}
|
||||
|
||||
public void setMoveSpeed(float moveSpeed) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.moveSpeed == moveSpeed) return;
|
||||
this.moveSpeed = moveSpeed;
|
||||
}
|
||||
|
||||
public float getMaxRemoveAngle() {
|
||||
return maxRemoveAngle;
|
||||
}
|
||||
|
||||
public void setMaxRemoveAngle(float maxRemoveAngle) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.maxRemoveAngle == maxRemoveAngle) return;
|
||||
this.maxRemoveAngle = maxRemoveAngle;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public float getMinRemoveAngle() {
|
||||
return minRemoveAngle;
|
||||
}
|
||||
|
||||
public void setMinRemoveAngle(float minRemoveAngle) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.minRemoveAngle == minRemoveAngle) return;
|
||||
this.minRemoveAngle = minRemoveAngle;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public int getGravity() {
|
||||
return gravity;
|
||||
}
|
||||
|
||||
public void setGravity(int gravity) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
assertGravity(gravity);
|
||||
if (this.gravity == gravity) return;
|
||||
this.gravity = gravity;
|
||||
if (gravity == LEFT || gravity == RIGHT) {
|
||||
setOrientation(RecyclerView.VERTICAL);
|
||||
} else {
|
||||
setOrientation(RecyclerView.HORIZONTAL);
|
||||
}
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public boolean getFlipRotate() {
|
||||
return flipRotate;
|
||||
}
|
||||
|
||||
public void setFlipRotate(boolean flipRotate) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.flipRotate == flipRotate) return;
|
||||
this.flipRotate = flipRotate;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public int getZAlignment() {
|
||||
return zAlignment;
|
||||
}
|
||||
|
||||
public void setZAlignment(int zAlignment) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
assertZAlignmentState(zAlignment);
|
||||
if (this.zAlignment == zAlignment) return;
|
||||
this.zAlignment = zAlignment;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float setInterval() {
|
||||
return angleInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() {
|
||||
radius = radius == Builder.INVALID_VALUE ? mDecoratedMeasurementInOther : radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float maxRemoveOffset() {
|
||||
return maxRemoveAngle;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float minRemoveOffset() {
|
||||
return minRemoveAngle;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calItemLeft(View itemView, float targetOffset) {
|
||||
switch (gravity) {
|
||||
case LEFT:
|
||||
return (int) (radius * Math.sin(Math.toRadians(90 - targetOffset)) - radius);
|
||||
case RIGHT:
|
||||
return (int) (radius - radius * Math.sin(Math.toRadians(90 - targetOffset)));
|
||||
case TOP:
|
||||
case BOTTOM:
|
||||
default:
|
||||
return (int) (radius * Math.cos(Math.toRadians(90 - targetOffset)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calItemTop(View itemView, float targetOffset) {
|
||||
switch (gravity) {
|
||||
case LEFT:
|
||||
case RIGHT:
|
||||
return (int) (radius * Math.cos(Math.toRadians(90 - targetOffset)));
|
||||
case TOP:
|
||||
return (int) (radius * Math.sin(Math.toRadians(90 - targetOffset)) - radius);
|
||||
case BOTTOM:
|
||||
default:
|
||||
return (int) (radius - radius * Math.sin(Math.toRadians(90 - targetOffset)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setItemViewProperty(View itemView, float targetOffset) {
|
||||
float scale = 1f;
|
||||
switch (gravity) {
|
||||
case RIGHT:
|
||||
case TOP:
|
||||
if (flipRotate) {
|
||||
itemView.setRotation(targetOffset);
|
||||
if (targetOffset < angleInterval && targetOffset > -angleInterval) {
|
||||
float diff = Math.abs(Math.abs(itemView.getRotation() - angleInterval) - angleInterval);
|
||||
scale = (centerScale - 1f) / -angleInterval * diff + centerScale;
|
||||
}
|
||||
} else {
|
||||
itemView.setRotation(360 - targetOffset);
|
||||
if (targetOffset < angleInterval && targetOffset > -angleInterval) {
|
||||
float diff = Math.abs(Math.abs(360 - itemView.getRotation() - angleInterval) - angleInterval);
|
||||
scale = (centerScale - 1f) / -angleInterval * diff + centerScale;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LEFT:
|
||||
case BOTTOM:
|
||||
default:
|
||||
if (flipRotate) {
|
||||
itemView.setRotation(360 - targetOffset);
|
||||
if (targetOffset < angleInterval && targetOffset > -angleInterval) {
|
||||
float diff = Math.abs(Math.abs(360 - itemView.getRotation() - angleInterval) - angleInterval);
|
||||
scale = (centerScale - 1f) / -angleInterval * diff + centerScale;
|
||||
}
|
||||
} else {
|
||||
itemView.setRotation(targetOffset);
|
||||
if (targetOffset < angleInterval && targetOffset > -angleInterval) {
|
||||
float diff = Math.abs(Math.abs(itemView.getRotation() - angleInterval) - angleInterval);
|
||||
scale = (centerScale - 1f) / -angleInterval * diff + centerScale;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
itemView.setScaleX(scale);
|
||||
itemView.setScaleY(scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float setViewElevation(View itemView, float targetOffset) {
|
||||
if (zAlignment == LEFT_ON_TOP)
|
||||
return (540 - targetOffset) / 72;
|
||||
else if (zAlignment == RIGHT_ON_TOP)
|
||||
return (targetOffset - 540) / 72;
|
||||
else
|
||||
return (360 - Math.abs(targetOffset)) / 72;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getDistanceRatio() {
|
||||
if (moveSpeed == 0) return Float.MAX_VALUE;
|
||||
return 1 / moveSpeed;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private static final float SCALE_RATE = 1.2f;
|
||||
private static int INTERVAL_ANGLE = 30;// The default mInterval angle between each items
|
||||
private static float DISTANCE_RATIO = 10f; // Finger swipe distance divide item rotate angle
|
||||
private static int INVALID_VALUE = Integer.MIN_VALUE;
|
||||
|
||||
private int radius;
|
||||
private int angleInterval;
|
||||
private float centerScale;
|
||||
private float moveSpeed;
|
||||
private float maxRemoveAngle;
|
||||
private float minRemoveAngle;
|
||||
private boolean reverseLayout;
|
||||
private Context context;
|
||||
private int gravity;
|
||||
private boolean flipRotate;
|
||||
private int zAlignment;
|
||||
private int maxVisibleItemCount;
|
||||
private int distanceToBottom;
|
||||
|
||||
public Builder(Context context) {
|
||||
this.context = context;
|
||||
radius = INVALID_VALUE;
|
||||
angleInterval = INTERVAL_ANGLE;
|
||||
centerScale = SCALE_RATE;
|
||||
moveSpeed = 1 / DISTANCE_RATIO;
|
||||
maxRemoveAngle = 90;
|
||||
minRemoveAngle = -90;
|
||||
reverseLayout = false;
|
||||
flipRotate = false;
|
||||
gravity = BOTTOM;
|
||||
zAlignment = CENTER_ON_TOP;
|
||||
distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE;
|
||||
maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN;
|
||||
}
|
||||
|
||||
public Builder setRadius(int radius) {
|
||||
this.radius = radius;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAngleInterval(int angleInterval) {
|
||||
this.angleInterval = angleInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCenterScale(float centerScale) {
|
||||
this.centerScale = centerScale;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMoveSpeed(int moveSpeed) {
|
||||
this.moveSpeed = moveSpeed;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMaxRemoveAngle(float maxRemoveAngle) {
|
||||
this.maxRemoveAngle = maxRemoveAngle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMinRemoveAngle(float minRemoveAngle) {
|
||||
this.minRemoveAngle = minRemoveAngle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setReverseLayout(boolean reverseLayout) {
|
||||
this.reverseLayout = reverseLayout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setGravity(int gravity) {
|
||||
assertGravity(gravity);
|
||||
this.gravity = gravity;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFlipRotate(boolean flipRotate) {
|
||||
this.flipRotate = flipRotate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setZAlignment(int zAlignment) {
|
||||
assertZAlignmentState(zAlignment);
|
||||
this.zAlignment = zAlignment;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMaxVisibleItemCount(int maxVisibleItemCount) {
|
||||
this.maxVisibleItemCount = maxVisibleItemCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDistanceToBottom(int distanceToBottom) {
|
||||
this.distanceToBottom = distanceToBottom;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CircleScaleLayoutManager build() {
|
||||
return new CircleScaleLayoutManager(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,288 @@
|
||||
package com.common.commonlib.view.viewpagerlayoutmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ViewPagerLayoutManager}
|
||||
* which will change rotate x or rotate y
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class GalleryLayoutManager extends ViewPagerLayoutManager {
|
||||
private final float MAX_ELEVATION = 5F;
|
||||
|
||||
private int itemSpace;
|
||||
private float moveSpeed;
|
||||
private float maxAlpha;
|
||||
private float minAlpha;
|
||||
private float angle;
|
||||
private boolean flipRotate;
|
||||
private boolean rotateFromEdge;
|
||||
|
||||
public GalleryLayoutManager(Context context, int itemSpace) {
|
||||
this(new Builder(context, itemSpace));
|
||||
}
|
||||
|
||||
public GalleryLayoutManager(Context context, int itemSpace, int orientation) {
|
||||
this(new Builder(context, itemSpace).setOrientation(orientation));
|
||||
}
|
||||
|
||||
public GalleryLayoutManager(Context context, int itemSpace, int orientation, boolean reverseLayout) {
|
||||
this(new Builder(context, itemSpace).setOrientation(orientation).setReverseLayout(reverseLayout));
|
||||
}
|
||||
|
||||
public GalleryLayoutManager(Builder builder) {
|
||||
this(builder.context, builder.itemSpace, builder.angle, builder.maxAlpha, builder.minAlpha,
|
||||
builder.orientation, builder.moveSpeed, builder.flipRotate, builder.rotateFromEdge,
|
||||
builder.maxVisibleItemCount, builder.distanceToBottom, builder.reverseLayout);
|
||||
}
|
||||
|
||||
private GalleryLayoutManager(Context context, int itemSpace, float angle, float maxAlpha, float minAlpha,
|
||||
int orientation, float moveSpeed, boolean flipRotate, boolean rotateFromEdge,
|
||||
int maxVisibleItemCount, int distanceToBottom, boolean reverseLayout) {
|
||||
super(context, orientation, reverseLayout);
|
||||
setDistanceToBottom(distanceToBottom);
|
||||
setMaxVisibleItemCount(maxVisibleItemCount);
|
||||
this.itemSpace = itemSpace;
|
||||
this.moveSpeed = moveSpeed;
|
||||
this.angle = angle;
|
||||
this.maxAlpha = maxAlpha;
|
||||
this.minAlpha = minAlpha;
|
||||
this.flipRotate = flipRotate;
|
||||
this.rotateFromEdge = rotateFromEdge;
|
||||
}
|
||||
|
||||
public int getItemSpace() {
|
||||
return itemSpace;
|
||||
}
|
||||
|
||||
public void setItemSpace(int itemSpace) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.itemSpace == itemSpace) return;
|
||||
this.itemSpace = itemSpace;
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
public float getMaxAlpha() {
|
||||
return maxAlpha;
|
||||
}
|
||||
|
||||
public void setMaxAlpha(float maxAlpha) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (maxAlpha > 1) maxAlpha = 1;
|
||||
if (this.maxAlpha == maxAlpha) return;
|
||||
this.maxAlpha = maxAlpha;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public float getMinAlpha() {
|
||||
return minAlpha;
|
||||
}
|
||||
|
||||
public void setMinAlpha(float minAlpha) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (minAlpha < 0) minAlpha = 0;
|
||||
if (this.minAlpha == minAlpha) return;
|
||||
this.minAlpha = minAlpha;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public float getAngle() {
|
||||
return angle;
|
||||
}
|
||||
|
||||
public void setAngle(float angle) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.angle == angle) return;
|
||||
this.angle = angle;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public float getMoveSpeed() {
|
||||
return moveSpeed;
|
||||
}
|
||||
|
||||
public void setMoveSpeed(float moveSpeed) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.moveSpeed == moveSpeed) return;
|
||||
this.moveSpeed = moveSpeed;
|
||||
}
|
||||
|
||||
public boolean getFlipRotate() {
|
||||
return flipRotate;
|
||||
}
|
||||
|
||||
public void setFlipRotate(boolean flipRotate) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.flipRotate == flipRotate) return;
|
||||
this.flipRotate = flipRotate;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public boolean getRotateFromEdge() {
|
||||
return rotateFromEdge;
|
||||
}
|
||||
|
||||
public void setRotateFromEdge(boolean rotateFromEdge) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.rotateFromEdge == rotateFromEdge) return;
|
||||
this.rotateFromEdge = rotateFromEdge;
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float setInterval() {
|
||||
return mDecoratedMeasurement + itemSpace;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setItemViewProperty(View itemView, float targetOffset) {
|
||||
final float rotation = calRotation(targetOffset);
|
||||
if (getOrientation() == RecyclerView.HORIZONTAL) {
|
||||
if (rotateFromEdge) {
|
||||
itemView.setPivotX(rotation > 0 ? 0 : mDecoratedMeasurement);
|
||||
itemView.setPivotY(mDecoratedMeasurementInOther * 0.5f);
|
||||
}
|
||||
if (flipRotate) {
|
||||
itemView.setRotationX(rotation);
|
||||
} else {
|
||||
itemView.setRotationY(rotation);
|
||||
}
|
||||
} else {
|
||||
if (rotateFromEdge) {
|
||||
itemView.setPivotY(rotation > 0 ? 0 : mDecoratedMeasurement);
|
||||
itemView.setPivotX(mDecoratedMeasurementInOther * 0.5f);
|
||||
|
||||
}
|
||||
if (flipRotate) {
|
||||
itemView.setRotationY(-rotation);
|
||||
} else {
|
||||
itemView.setRotationX(-rotation);
|
||||
}
|
||||
}
|
||||
final float alpha = calAlpha(targetOffset);
|
||||
itemView.setAlpha(alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float setViewElevation(View itemView, float targetOffset) {
|
||||
final float ele = Math.max(Math.abs(itemView.getRotationX()), Math.abs(itemView.getRotationY())) * MAX_ELEVATION / 360;
|
||||
return MAX_ELEVATION - ele;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getDistanceRatio() {
|
||||
if (moveSpeed == 0) return Float.MAX_VALUE;
|
||||
return 1 / moveSpeed;
|
||||
}
|
||||
|
||||
private float calRotation(float targetOffset) {
|
||||
return -angle / mInterval * targetOffset;
|
||||
}
|
||||
|
||||
private float calAlpha(float targetOffset) {
|
||||
final float offset = Math.abs(targetOffset);
|
||||
float alpha = (minAlpha - maxAlpha) / mInterval * offset + maxAlpha;
|
||||
if (offset >= mInterval) alpha = minAlpha;
|
||||
return alpha;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private static final float DEFAULT_SPEED = 1f;
|
||||
private static float INTERVAL_ANGLE = 30f;
|
||||
private static float MIN_ALPHA = 0.5f;
|
||||
private static float MAX_ALPHA = 1f;
|
||||
|
||||
private int itemSpace;
|
||||
private float moveSpeed;
|
||||
private int orientation;
|
||||
private float maxAlpha;
|
||||
private float minAlpha;
|
||||
private float angle;
|
||||
private boolean flipRotate;
|
||||
private boolean reverseLayout;
|
||||
private Context context;
|
||||
private int maxVisibleItemCount;
|
||||
private int distanceToBottom;
|
||||
private boolean rotateFromEdge;
|
||||
|
||||
public Builder(Context context, int itemSpace) {
|
||||
this.itemSpace = itemSpace;
|
||||
this.context = context;
|
||||
orientation = HORIZONTAL;
|
||||
angle = INTERVAL_ANGLE;
|
||||
maxAlpha = MAX_ALPHA;
|
||||
minAlpha = MIN_ALPHA;
|
||||
this.moveSpeed = DEFAULT_SPEED;
|
||||
reverseLayout = false;
|
||||
flipRotate = false;
|
||||
rotateFromEdge = false;
|
||||
distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE;
|
||||
maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN;
|
||||
}
|
||||
|
||||
public Builder setItemSpace(int itemSpace) {
|
||||
this.itemSpace = itemSpace;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMoveSpeed(float moveSpeed) {
|
||||
this.moveSpeed = moveSpeed;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setOrientation(int orientation) {
|
||||
this.orientation = orientation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMaxAlpha(float maxAlpha) {
|
||||
if (maxAlpha > 1) maxAlpha = 1;
|
||||
this.maxAlpha = maxAlpha;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMinAlpha(float minAlpha) {
|
||||
if (minAlpha < 0) minAlpha = 0;
|
||||
this.minAlpha = minAlpha;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAngle(float angle) {
|
||||
this.angle = angle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFlipRotate(boolean flipRotate) {
|
||||
this.flipRotate = flipRotate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setReverseLayout(boolean reverseLayout) {
|
||||
this.reverseLayout = reverseLayout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMaxVisibleItemCount(int maxVisibleItemCount) {
|
||||
this.maxVisibleItemCount = maxVisibleItemCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDistanceToBottom(int distanceToBottom) {
|
||||
this.distanceToBottom = distanceToBottom;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRotateFromEdge(boolean rotateFromEdge) {
|
||||
this.rotateFromEdge = rotateFromEdge;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GalleryLayoutManager build() {
|
||||
return new GalleryLayoutManager(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.common.commonlib.view.viewpagerlayoutmanager;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
@SuppressWarnings({"WeakerAccess","unused"})
|
||||
public abstract class OrientationHelper {
|
||||
|
||||
private static final int INVALID_SIZE = Integer.MIN_VALUE;
|
||||
|
||||
protected final RecyclerView.LayoutManager mLayoutManager;
|
||||
|
||||
public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
|
||||
|
||||
public static final int VERTICAL = LinearLayout.VERTICAL;
|
||||
|
||||
private int mLastTotalSpace = INVALID_SIZE;
|
||||
|
||||
final Rect mTmpRect = new Rect();
|
||||
|
||||
private OrientationHelper(RecyclerView.LayoutManager layoutManager) {
|
||||
mLayoutManager = layoutManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method after onLayout method is complete if state is NOT pre-layout.
|
||||
* This method records information like layout bounds that might be useful in the next layout
|
||||
* calculations.
|
||||
*/
|
||||
public void onLayoutComplete() {
|
||||
mLastTotalSpace = getTotalSpace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the layout space change between the previous layout pass and current layout pass.
|
||||
* <p>
|
||||
* Make sure you call {@link #onLayoutComplete()} at the end of your LayoutManager's
|
||||
* {@link RecyclerView.LayoutManager#onLayoutChildren(RecyclerView.Recycler,
|
||||
* RecyclerView.State)} method.
|
||||
*
|
||||
* @return The difference between the current total space and previous layout's total space.
|
||||
* @see #onLayoutComplete()
|
||||
*/
|
||||
public int getTotalSpaceChange() {
|
||||
return INVALID_SIZE == mLastTotalSpace ? 0 : getTotalSpace() - mLastTotalSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the start of the view including its decoration and margin.
|
||||
* <p>
|
||||
* For example, for the horizontal helper, if a View's left is at pixel 20, has 2px left
|
||||
* decoration and 3px left margin, returned value will be 15px.
|
||||
*
|
||||
* @param view The view element to check
|
||||
* @return The first pixel of the element
|
||||
* @see #getDecoratedEnd(View)
|
||||
*/
|
||||
public abstract int getDecoratedStart(View view);
|
||||
|
||||
/**
|
||||
* Returns the end of the view including its decoration and margin.
|
||||
* <p>
|
||||
* For example, for the horizontal helper, if a View's right is at pixel 200, has 2px right
|
||||
* decoration and 3px right margin, returned value will be 205.
|
||||
*
|
||||
* @param view The view element to check
|
||||
* @return The last pixel of the element
|
||||
* @see #getDecoratedStart(View)
|
||||
*/
|
||||
public abstract int getDecoratedEnd(View view);
|
||||
|
||||
/**
|
||||
* Returns the end of the View after its matrix transformations are applied to its layout
|
||||
* position.
|
||||
* <p>
|
||||
* This method is useful when trying to detect the visible edge of a View.
|
||||
* <p>
|
||||
* It includes the decorations but does not include the margins.
|
||||
*
|
||||
* @param view The view whose transformed end will be returned
|
||||
* @return The end of the View after its decor insets and transformation matrix is applied to
|
||||
* its position
|
||||
* @see RecyclerView.LayoutManager#getTransformedBoundingBox(View, boolean, Rect)
|
||||
*/
|
||||
public abstract int getTransformedEndWithDecoration(View view);
|
||||
|
||||
/**
|
||||
* Returns the start of the View after its matrix transformations are applied to its layout
|
||||
* position.
|
||||
* <p>
|
||||
* This method is useful when trying to detect the visible edge of a View.
|
||||
* <p>
|
||||
* It includes the decorations but does not include the margins.
|
||||
*
|
||||
* @param view The view whose transformed start will be returned
|
||||
* @return The start of the View after its decor insets and transformation matrix is applied to
|
||||
* its position
|
||||
* @see RecyclerView.LayoutManager#getTransformedBoundingBox(View, boolean, Rect)
|
||||
*/
|
||||
public abstract int getTransformedStartWithDecoration(View view);
|
||||
|
||||
/**
|
||||
* Returns the space occupied by this View in the current orientation including decorations and
|
||||
* margins.
|
||||
*
|
||||
* @param view The view element to check
|
||||
* @return Total space occupied by this view
|
||||
* @see #getDecoratedMeasurementInOther(View)
|
||||
*/
|
||||
public abstract int getDecoratedMeasurement(View view);
|
||||
|
||||
/**
|
||||
* Returns the space occupied by this View in the perpendicular orientation including
|
||||
* decorations and margins.
|
||||
*
|
||||
* @param view The view element to check
|
||||
* @return Total space occupied by this view in the perpendicular orientation to current one
|
||||
* @see #getDecoratedMeasurement(View)
|
||||
*/
|
||||
public abstract int getDecoratedMeasurementInOther(View view);
|
||||
|
||||
/**
|
||||
* Returns the start position of the layout after the start padding is added.
|
||||
*
|
||||
* @return The very first pixel we can draw.
|
||||
*/
|
||||
public abstract int getStartAfterPadding();
|
||||
|
||||
/**
|
||||
* Returns the end position of the layout after the end padding is removed.
|
||||
*
|
||||
* @return The end boundary for this layout.
|
||||
*/
|
||||
public abstract int getEndAfterPadding();
|
||||
|
||||
/**
|
||||
* Returns the end position of the layout without taking padding into account.
|
||||
*
|
||||
* @return The end boundary for this layout without considering padding.
|
||||
*/
|
||||
public abstract int getEnd();
|
||||
|
||||
/**
|
||||
* Returns the total space to layout. This number is the difference between
|
||||
* {@link #getEndAfterPadding()} and {@link #getStartAfterPadding()}.
|
||||
*
|
||||
* @return Total space to layout children
|
||||
*/
|
||||
public abstract int getTotalSpace();
|
||||
|
||||
/**
|
||||
* Returns the total space in other direction to layout. This number is the difference between
|
||||
* {@link #getEndAfterPadding()} and {@link #getStartAfterPadding()}.
|
||||
*
|
||||
* @return Total space to layout children
|
||||
*/
|
||||
public abstract int getTotalSpaceInOther();
|
||||
|
||||
/**
|
||||
* Returns the padding at the end of the layout. For horizontal helper, this is the right
|
||||
* padding and for vertical helper, this is the bottom padding. This method does not check
|
||||
* whether the layout is RTL or not.
|
||||
*
|
||||
* @return The padding at the end of the layout.
|
||||
*/
|
||||
public abstract int getEndPadding();
|
||||
|
||||
/**
|
||||
* Returns the MeasureSpec mode for the current orientation from the LayoutManager.
|
||||
*
|
||||
* @return The current measure spec mode.
|
||||
* @see View.MeasureSpec
|
||||
* @see RecyclerView.LayoutManager#getWidthMode()
|
||||
* @see RecyclerView.LayoutManager#getHeightMode()
|
||||
*/
|
||||
public abstract int getMode();
|
||||
|
||||
/**
|
||||
* Returns the MeasureSpec mode for the perpendicular orientation from the LayoutManager.
|
||||
*
|
||||
* @return The current measure spec mode.
|
||||
* @see View.MeasureSpec
|
||||
* @see RecyclerView.LayoutManager#getWidthMode()
|
||||
* @see RecyclerView.LayoutManager#getHeightMode()
|
||||
*/
|
||||
public abstract int getModeInOther();
|
||||
|
||||
/**
|
||||
* Creates an OrientationHelper for the given LayoutManager and orientation.
|
||||
*
|
||||
* @param layoutManager LayoutManager to attach to
|
||||
* @param orientation Desired orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}
|
||||
* @return A new OrientationHelper
|
||||
*/
|
||||
public static OrientationHelper createOrientationHelper(
|
||||
RecyclerView.LayoutManager layoutManager, int orientation) {
|
||||
switch (orientation) {
|
||||
case HORIZONTAL:
|
||||
return createHorizontalHelper(layoutManager);
|
||||
case VERTICAL:
|
||||
return createVerticalHelper(layoutManager);
|
||||
}
|
||||
throw new IllegalArgumentException("invalid orientation");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a horizontal OrientationHelper for the given LayoutManager.
|
||||
*
|
||||
* @param layoutManager The LayoutManager to attach to.
|
||||
* @return A new OrientationHelper
|
||||
*/
|
||||
public static OrientationHelper createHorizontalHelper(
|
||||
RecyclerView.LayoutManager layoutManager) {
|
||||
return new OrientationHelper(layoutManager) {
|
||||
@Override
|
||||
public int getEndAfterPadding() {
|
||||
return mLayoutManager.getWidth() - mLayoutManager.getPaddingRight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEnd() {
|
||||
return mLayoutManager.getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartAfterPadding() {
|
||||
return mLayoutManager.getPaddingLeft();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDecoratedMeasurement(View view) {
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
|
||||
view.getLayoutParams();
|
||||
return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin
|
||||
+ params.rightMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDecoratedMeasurementInOther(View view) {
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
|
||||
view.getLayoutParams();
|
||||
return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin
|
||||
+ params.bottomMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDecoratedEnd(View view) {
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
|
||||
view.getLayoutParams();
|
||||
return mLayoutManager.getDecoratedRight(view) + params.rightMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDecoratedStart(View view) {
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
|
||||
view.getLayoutParams();
|
||||
return mLayoutManager.getDecoratedLeft(view) - params.leftMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTransformedEndWithDecoration(View view) {
|
||||
mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
|
||||
return mTmpRect.right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTransformedStartWithDecoration(View view) {
|
||||
mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
|
||||
return mTmpRect.left;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalSpace() {
|
||||
return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft()
|
||||
- mLayoutManager.getPaddingRight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalSpaceInOther() {
|
||||
return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop()
|
||||
- mLayoutManager.getPaddingBottom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndPadding() {
|
||||
return mLayoutManager.getPaddingRight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMode() {
|
||||
return mLayoutManager.getWidthMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModeInOther() {
|
||||
return mLayoutManager.getHeightMode();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a vertical OrientationHelper for the given LayoutManager.
|
||||
*
|
||||
* @param layoutManager The LayoutManager to attach to.
|
||||
* @return A new OrientationHelper
|
||||
*/
|
||||
public static OrientationHelper createVerticalHelper(RecyclerView.LayoutManager layoutManager) {
|
||||
return new OrientationHelper(layoutManager) {
|
||||
@Override
|
||||
public int getEndAfterPadding() {
|
||||
return mLayoutManager.getHeight() - mLayoutManager.getPaddingBottom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEnd() {
|
||||
return mLayoutManager.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartAfterPadding() {
|
||||
return mLayoutManager.getPaddingTop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDecoratedMeasurement(View view) {
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
|
||||
view.getLayoutParams();
|
||||
return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin
|
||||
+ params.bottomMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDecoratedMeasurementInOther(View view) {
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
|
||||
view.getLayoutParams();
|
||||
return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin
|
||||
+ params.rightMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDecoratedEnd(View view) {
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
|
||||
view.getLayoutParams();
|
||||
return mLayoutManager.getDecoratedBottom(view) + params.bottomMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDecoratedStart(View view) {
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
|
||||
view.getLayoutParams();
|
||||
return mLayoutManager.getDecoratedTop(view) - params.topMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTransformedEndWithDecoration(View view) {
|
||||
mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
|
||||
return mTmpRect.bottom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTransformedStartWithDecoration(View view) {
|
||||
mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
|
||||
return mTmpRect.top;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalSpace() {
|
||||
return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop()
|
||||
- mLayoutManager.getPaddingBottom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalSpaceInOther() {
|
||||
return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft()
|
||||
- mLayoutManager.getPaddingRight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndPadding() {
|
||||
return mLayoutManager.getPaddingBottom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMode() {
|
||||
return mLayoutManager.getHeightMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModeInOther() {
|
||||
return mLayoutManager.getWidthMode();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package com.common.commonlib.view.viewpagerlayoutmanager;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class PageSnapHelper extends CenterSnapHelper {
|
||||
|
||||
@Override
|
||||
public boolean onFling(int velocityX, int velocityY) {
|
||||
ViewPagerLayoutManager layoutManager = (ViewPagerLayoutManager) mRecyclerView.getLayoutManager();
|
||||
if (layoutManager == null) {
|
||||
return false;
|
||||
}
|
||||
RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
|
||||
if (adapter == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!layoutManager.getInfinite() &&
|
||||
(layoutManager.mOffset == layoutManager.getMaxOffset()
|
||||
|| layoutManager.mOffset == layoutManager.getMinOffset())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int minFlingVelocity = mRecyclerView.getMinFlingVelocity();
|
||||
mGravityScroller.fling(0, 0, velocityX, velocityY,
|
||||
Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
|
||||
|
||||
if (layoutManager.mOrientation == ViewPagerLayoutManager.VERTICAL
|
||||
&& Math.abs(velocityY) > minFlingVelocity) {
|
||||
final int currentPosition = layoutManager.getCurrentPositionOffset();
|
||||
final int offsetPosition = mGravityScroller.getFinalY() * layoutManager.getDistanceRatio() > layoutManager.mInterval ? 1 : 0;
|
||||
ScrollHelper.smoothScrollToPosition(mRecyclerView, layoutManager, layoutManager.getReverseLayout() ?
|
||||
-currentPosition - offsetPosition : currentPosition + offsetPosition);
|
||||
return true;
|
||||
} else if (layoutManager.mOrientation == ViewPagerLayoutManager.HORIZONTAL
|
||||
&& Math.abs(velocityX) > minFlingVelocity) {
|
||||
final int currentPosition = layoutManager.getCurrentPositionOffset();
|
||||
final int offsetPosition = mGravityScroller.getFinalX() * layoutManager.getDistanceRatio() > layoutManager.mInterval ? 1 : 0;
|
||||
ScrollHelper.smoothScrollToPosition(mRecyclerView, layoutManager, layoutManager.getReverseLayout() ?
|
||||
-currentPosition - offsetPosition : currentPosition + offsetPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,178 @@
|
||||
package com.common.commonlib.view.viewpagerlayoutmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ViewPagerLayoutManager}
|
||||
* which rotates items
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class RotateLayoutManager extends ViewPagerLayoutManager {
|
||||
|
||||
private int itemSpace;
|
||||
private float angle;
|
||||
private float moveSpeed;
|
||||
private boolean reverseRotate;
|
||||
|
||||
public RotateLayoutManager(Context context, int itemSpace) {
|
||||
this(new Builder(context, itemSpace));
|
||||
}
|
||||
|
||||
public RotateLayoutManager(Context context, int itemSpace, int orientation) {
|
||||
this(new Builder(context, itemSpace).setOrientation(orientation));
|
||||
}
|
||||
|
||||
public RotateLayoutManager(Context context, int itemSpace, int orientation, boolean reverseLayout) {
|
||||
this(new Builder(context, itemSpace).setOrientation(orientation).setReverseLayout(reverseLayout));
|
||||
}
|
||||
|
||||
public RotateLayoutManager(Builder builder) {
|
||||
this(builder.context, builder.itemSpace, builder.angle, builder.orientation, builder.moveSpeed,
|
||||
builder.reverseRotate, builder.maxVisibleItemCount, builder.distanceToBottom,
|
||||
builder.reverseLayout);
|
||||
}
|
||||
|
||||
private RotateLayoutManager(Context context, int itemSpace, float angle, int orientation, float moveSpeed,
|
||||
boolean reverseRotate, int maxVisibleItemCount, int distanceToBottom,
|
||||
boolean reverseLayout) {
|
||||
super(context, orientation, reverseLayout);
|
||||
setDistanceToBottom(distanceToBottom);
|
||||
setMaxVisibleItemCount(maxVisibleItemCount);
|
||||
this.itemSpace = itemSpace;
|
||||
this.angle = angle;
|
||||
this.moveSpeed = moveSpeed;
|
||||
this.reverseRotate = reverseRotate;
|
||||
}
|
||||
|
||||
public int getItemSpace() {
|
||||
return itemSpace;
|
||||
}
|
||||
|
||||
public float getAngle() {
|
||||
return angle;
|
||||
}
|
||||
|
||||
public float getMoveSpeed() {
|
||||
return moveSpeed;
|
||||
}
|
||||
|
||||
public boolean getReverseRotate() {
|
||||
return reverseRotate;
|
||||
}
|
||||
|
||||
public void setItemSpace(int itemSpace) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.itemSpace == itemSpace) return;
|
||||
this.itemSpace = itemSpace;
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
public void setAngle(float centerScale) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.angle == centerScale) return;
|
||||
this.angle = centerScale;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void setMoveSpeed(float moveSpeed) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.moveSpeed == moveSpeed) return;
|
||||
this.moveSpeed = moveSpeed;
|
||||
}
|
||||
|
||||
public void setReverseRotate(boolean reverseRotate) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.reverseRotate == reverseRotate) return;
|
||||
this.reverseRotate = reverseRotate;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float setInterval() {
|
||||
return mDecoratedMeasurement + itemSpace;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setItemViewProperty(View itemView, float targetOffset) {
|
||||
itemView.setRotation(calRotation(targetOffset));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getDistanceRatio() {
|
||||
if (moveSpeed == 0) return Float.MAX_VALUE;
|
||||
return 1 / moveSpeed;
|
||||
}
|
||||
|
||||
private float calRotation(float targetOffset) {
|
||||
final float realAngle = reverseRotate ? angle : -angle;
|
||||
return realAngle / mInterval * targetOffset;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private static float INTERVAL_ANGLE = 360f;
|
||||
private static final float DEFAULT_SPEED = 1f;
|
||||
|
||||
private int itemSpace;
|
||||
private int orientation;
|
||||
private float angle;
|
||||
private float moveSpeed;
|
||||
private boolean reverseRotate;
|
||||
private boolean reverseLayout;
|
||||
private Context context;
|
||||
private int maxVisibleItemCount;
|
||||
private int distanceToBottom;
|
||||
|
||||
public Builder(Context context, int itemSpace) {
|
||||
this.context = context;
|
||||
this.itemSpace = itemSpace;
|
||||
orientation = HORIZONTAL;
|
||||
angle = INTERVAL_ANGLE;
|
||||
this.moveSpeed = DEFAULT_SPEED;
|
||||
reverseRotate = false;
|
||||
reverseLayout = false;
|
||||
distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE;
|
||||
maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN;
|
||||
}
|
||||
|
||||
public Builder setOrientation(int orientation) {
|
||||
this.orientation = orientation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAngle(float angle) {
|
||||
this.angle = angle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setReverseLayout(boolean reverseLayout) {
|
||||
this.reverseLayout = reverseLayout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMoveSpeed(float moveSpeed) {
|
||||
this.moveSpeed = moveSpeed;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setReverseRotate(boolean reverseRotate) {
|
||||
this.reverseRotate = reverseRotate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMaxVisibleItemCount(int maxVisibleItemCount) {
|
||||
this.maxVisibleItemCount = maxVisibleItemCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDistanceToBottom(int distanceToBottom) {
|
||||
this.distanceToBottom = distanceToBottom;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RotateLayoutManager build() {
|
||||
return new RotateLayoutManager(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
package com.common.commonlib.view.viewpagerlayoutmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ViewPagerLayoutManager}
|
||||
* which zooms the center item
|
||||
*/
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class ScaleLayoutManager extends ViewPagerLayoutManager {
|
||||
|
||||
private int itemSpace;
|
||||
private float minScale;
|
||||
private float moveSpeed;
|
||||
private float maxAlpha;
|
||||
private float minAlpha;
|
||||
|
||||
public ScaleLayoutManager(Context context, int itemSpace) {
|
||||
this(new Builder(context, itemSpace));
|
||||
}
|
||||
|
||||
public ScaleLayoutManager(Context context, int itemSpace, int orientation) {
|
||||
this(new Builder(context, itemSpace).setOrientation(orientation));
|
||||
}
|
||||
|
||||
public ScaleLayoutManager(Context context, int itemSpace, int orientation, boolean reverseLayout) {
|
||||
this(new Builder(context, itemSpace).setOrientation(orientation).setReverseLayout(reverseLayout));
|
||||
}
|
||||
|
||||
public ScaleLayoutManager(Builder builder) {
|
||||
this(builder.context, builder.itemSpace, builder.minScale, builder.maxAlpha, builder.minAlpha,
|
||||
builder.orientation, builder.moveSpeed, builder.maxVisibleItemCount, builder.distanceToBottom,
|
||||
builder.reverseLayout);
|
||||
}
|
||||
|
||||
private ScaleLayoutManager(Context context, int itemSpace, float minScale, float maxAlpha, float minAlpha,
|
||||
int orientation, float moveSpeed, int maxVisibleItemCount, int distanceToBottom,
|
||||
boolean reverseLayout) {
|
||||
super(context, orientation, reverseLayout);
|
||||
setDistanceToBottom(distanceToBottom);
|
||||
setMaxVisibleItemCount(maxVisibleItemCount);
|
||||
this.itemSpace = itemSpace;
|
||||
this.minScale = minScale;
|
||||
this.moveSpeed = moveSpeed;
|
||||
this.maxAlpha = maxAlpha;
|
||||
this.minAlpha = minAlpha;
|
||||
}
|
||||
|
||||
public int getItemSpace() {
|
||||
return itemSpace;
|
||||
}
|
||||
|
||||
public float getMinScale() {
|
||||
return minScale;
|
||||
}
|
||||
|
||||
public float getMoveSpeed() {
|
||||
return moveSpeed;
|
||||
}
|
||||
|
||||
public float getMaxAlpha() {
|
||||
return maxAlpha;
|
||||
}
|
||||
|
||||
public float getMinAlpha() {
|
||||
return minAlpha;
|
||||
}
|
||||
|
||||
public void setItemSpace(int itemSpace) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.itemSpace == itemSpace) return;
|
||||
this.itemSpace = itemSpace;
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
public void setMinScale(float minScale) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.minScale == minScale) return;
|
||||
this.minScale = minScale;
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
public void setMaxAlpha(float maxAlpha) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (maxAlpha > 1) maxAlpha = 1;
|
||||
if (this.maxAlpha == maxAlpha) return;
|
||||
this.maxAlpha = maxAlpha;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void setMinAlpha(float minAlpha) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (minAlpha < 0) minAlpha = 0;
|
||||
if (this.minAlpha == minAlpha) return;
|
||||
this.minAlpha = minAlpha;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void setMoveSpeed(float moveSpeed) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.moveSpeed == moveSpeed) return;
|
||||
this.moveSpeed = moveSpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float setInterval() {
|
||||
return itemSpace + mDecoratedMeasurement;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setItemViewProperty(View itemView, float targetOffset) {
|
||||
float scale = calculateScale(targetOffset + mSpaceMain);
|
||||
itemView.setScaleX(scale);
|
||||
itemView.setScaleY(scale);
|
||||
final float alpha = calAlpha(targetOffset);
|
||||
itemView.setAlpha(alpha);
|
||||
}
|
||||
|
||||
private float calAlpha(float targetOffset) {
|
||||
final float offset = Math.abs(targetOffset);
|
||||
float alpha = (minAlpha - maxAlpha) / mInterval * offset + maxAlpha;
|
||||
if (offset >= mInterval) alpha = minAlpha;
|
||||
return alpha;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getDistanceRatio() {
|
||||
if (moveSpeed == 0) return Float.MAX_VALUE;
|
||||
return 1 / moveSpeed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x start positon of the view you want scale
|
||||
* @return the scale rate of current scroll mOffset
|
||||
*/
|
||||
private float calculateScale(float x) {
|
||||
float deltaX = Math.abs(x - mSpaceMain);
|
||||
if (deltaX - mDecoratedMeasurement > 0) deltaX = mDecoratedMeasurement;
|
||||
return 1f - deltaX / mDecoratedMeasurement * (1f - minScale);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private static final float SCALE_RATE = 0.8f;
|
||||
private static final float DEFAULT_SPEED = 1f;
|
||||
private static float MIN_ALPHA = 1f;
|
||||
private static float MAX_ALPHA = 1f;
|
||||
|
||||
private int itemSpace;
|
||||
private int orientation;
|
||||
private float minScale;
|
||||
private float moveSpeed;
|
||||
private float maxAlpha;
|
||||
private float minAlpha;
|
||||
private boolean reverseLayout;
|
||||
private Context context;
|
||||
private int maxVisibleItemCount;
|
||||
private int distanceToBottom;
|
||||
|
||||
public Builder(Context context, int itemSpace) {
|
||||
this.itemSpace = itemSpace;
|
||||
this.context = context;
|
||||
orientation = HORIZONTAL;
|
||||
minScale = SCALE_RATE;
|
||||
this.moveSpeed = DEFAULT_SPEED;
|
||||
maxAlpha = MAX_ALPHA;
|
||||
minAlpha = MIN_ALPHA;
|
||||
reverseLayout = false;
|
||||
distanceToBottom = ViewPagerLayoutManager.INVALID_SIZE;
|
||||
maxVisibleItemCount = ViewPagerLayoutManager.DETERMINE_BY_MAX_AND_MIN;
|
||||
}
|
||||
|
||||
public Builder setOrientation(int orientation) {
|
||||
this.orientation = orientation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMinScale(float minScale) {
|
||||
this.minScale = minScale;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setReverseLayout(boolean reverseLayout) {
|
||||
this.reverseLayout = reverseLayout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMaxAlpha(float maxAlpha) {
|
||||
if (maxAlpha > 1) maxAlpha = 1;
|
||||
this.maxAlpha = maxAlpha;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMinAlpha(float minAlpha) {
|
||||
if (minAlpha < 0) minAlpha = 0;
|
||||
this.minAlpha = minAlpha;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMoveSpeed(float moveSpeed) {
|
||||
this.moveSpeed = moveSpeed;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMaxVisibleItemCount(int maxVisibleItemCount) {
|
||||
this.maxVisibleItemCount = maxVisibleItemCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDistanceToBottom(int distanceToBottom) {
|
||||
this.distanceToBottom = distanceToBottom;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ScaleLayoutManager build() {
|
||||
return new ScaleLayoutManager(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
package com.common.commonlib.view.viewpagerlayoutmanager;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class ScrollHelper {
|
||||
/* package */
|
||||
static void smoothScrollToPosition(RecyclerView recyclerView, ViewPagerLayoutManager viewPagerLayoutManager, int targetPosition) {
|
||||
final int delta = viewPagerLayoutManager.getOffsetToPosition(targetPosition);
|
||||
if (viewPagerLayoutManager.getOrientation() == RecyclerView.VERTICAL) {
|
||||
recyclerView.smoothScrollBy(0, delta);
|
||||
} else {
|
||||
recyclerView.smoothScrollBy(delta, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void smoothScrollToTargetView(RecyclerView recyclerView, View targetView) {
|
||||
final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
|
||||
if (!(layoutManager instanceof ViewPagerLayoutManager)) return;
|
||||
final int targetPosition = ((ViewPagerLayoutManager) layoutManager).getLayoutPositionOfView(targetView);
|
||||
smoothScrollToPosition(recyclerView, (ViewPagerLayoutManager) layoutManager, targetPosition);
|
||||
}
|
||||
}
|
@ -0,0 +1,936 @@
|
||||
package com.common.commonlib.view.viewpagerlayoutmanager;
|
||||
|
||||
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue"})
|
||||
public abstract class ViewPagerLayoutManager extends LinearLayoutManager {
|
||||
|
||||
public static final int DETERMINE_BY_MAX_AND_MIN = -1;
|
||||
|
||||
public static final int HORIZONTAL = OrientationHelper.HORIZONTAL;
|
||||
|
||||
public static final int VERTICAL = OrientationHelper.VERTICAL;
|
||||
protected static final int INVALID_SIZE = Integer.MAX_VALUE;
|
||||
private static final int DIRECTION_NO_WHERE = -1;
|
||||
private static final int DIRECTION_FORWARD = 0;
|
||||
private static final int DIRECTION_BACKWARD = 1;
|
||||
protected int mDecoratedMeasurement;
|
||||
protected int mDecoratedMeasurementInOther;
|
||||
protected int mSpaceMain;
|
||||
protected int mSpaceInOther;
|
||||
/**
|
||||
* The offset of property which will change while scrolling
|
||||
*/
|
||||
protected float mOffset;
|
||||
/**
|
||||
* Many calculations are made depending on orientation. To keep it clean, this interface
|
||||
* helps {@link LinearLayoutManager} make those decisions.
|
||||
* Based on {@link #mOrientation}, an implementation is lazily created in
|
||||
* method.
|
||||
*/
|
||||
protected OrientationHelper mOrientationHelper;
|
||||
protected float mInterval; //the mInterval of each item's mOffset
|
||||
/**
|
||||
* Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}
|
||||
*/
|
||||
int mOrientation;
|
||||
/* package */ OnPageChangeListener onPageChangeListener;
|
||||
private SparseArray<View> positionCache = new SparseArray<>();
|
||||
/**
|
||||
* Defines if layout should be calculated from end to start.
|
||||
*/
|
||||
private boolean mReverseLayout = false;
|
||||
/**
|
||||
* This keeps the final value for how LayoutManager should start laying out views.
|
||||
* It is calculated by checking {@link #getReverseLayout()} and View's layout direction.
|
||||
* {@link #onLayoutChildren(RecyclerView.Recycler, RecyclerView.State)} is run.
|
||||
*/
|
||||
private boolean mShouldReverseLayout = false;
|
||||
/**
|
||||
* Works the same way as {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}.
|
||||
* see {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}
|
||||
*/
|
||||
private boolean mSmoothScrollbarEnabled = true;
|
||||
/**
|
||||
* When LayoutManager needs to scroll to a position, it sets this variable and requests a
|
||||
* layout which will check this variable and re-layout accordingly.
|
||||
*/
|
||||
private int mPendingScrollPosition = NO_POSITION;
|
||||
private SavedState mPendingSavedState = null;
|
||||
private boolean mRecycleChildrenOnDetach;
|
||||
|
||||
private boolean mInfinite = false;
|
||||
|
||||
private boolean mEnableBringCenterToFront;
|
||||
|
||||
private int mLeftItems;
|
||||
|
||||
private int mRightItems;
|
||||
|
||||
/**
|
||||
* max visible item count
|
||||
*/
|
||||
private int mMaxVisibleItemCount = DETERMINE_BY_MAX_AND_MIN;
|
||||
|
||||
private Interpolator mSmoothScrollInterpolator;
|
||||
|
||||
private int mDistanceToBottom = INVALID_SIZE;
|
||||
|
||||
/**
|
||||
* use for handle focus
|
||||
*/
|
||||
private View currentFocusView;
|
||||
|
||||
/**
|
||||
* Creates a horizontal ViewPagerLayoutManager
|
||||
*/
|
||||
public ViewPagerLayoutManager(Context context) {
|
||||
this(context, HORIZONTAL, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}
|
||||
* @param reverseLayout When set to true, layouts from end to start
|
||||
*/
|
||||
public ViewPagerLayoutManager(Context context, int orientation, boolean reverseLayout) {
|
||||
super(context);
|
||||
setOrientation(orientation);
|
||||
setReverseLayout(reverseLayout);
|
||||
setAutoMeasureEnabled(true);
|
||||
setItemPrefetchEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the mInterval of each item's mOffset
|
||||
*/
|
||||
protected abstract float setInterval();
|
||||
|
||||
protected abstract void setItemViewProperty(View itemView, float targetOffset);
|
||||
|
||||
/**
|
||||
* cause elevation is not support below api 21,
|
||||
* so you can set your elevation here for supporting it below api 21
|
||||
* or you can just setElevation in {@link #setItemViewProperty(View, float)}
|
||||
*/
|
||||
protected float setViewElevation(View itemView, float targetOffset) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
|
||||
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether LayoutManager will recycle its children when it is detached from
|
||||
* RecyclerView.
|
||||
*
|
||||
* @return true if LayoutManager will recycle its children when it is detached from
|
||||
* RecyclerView.
|
||||
*/
|
||||
public boolean getRecycleChildrenOnDetach() {
|
||||
return mRecycleChildrenOnDetach;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether LayoutManager will recycle its children when it is detached from
|
||||
* RecyclerView.
|
||||
* <p>
|
||||
* If you are using a {@link RecyclerView.RecycledViewPool}, it might be a good idea to set
|
||||
* this flag to <code>true</code> so that views will be available to other RecyclerViews
|
||||
* immediately.
|
||||
* <p>
|
||||
* Note that, setting this flag will result in a performance drop if RecyclerView
|
||||
* is restored.
|
||||
*
|
||||
* @param recycleChildrenOnDetach Whether children should be recycled in detach or not.
|
||||
*/
|
||||
public void setRecycleChildrenOnDetach(boolean recycleChildrenOnDetach) {
|
||||
mRecycleChildrenOnDetach = recycleChildrenOnDetach;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
|
||||
super.onDetachedFromWindow(view, recycler);
|
||||
if (mRecycleChildrenOnDetach) {
|
||||
removeAndRecycleAllViews(recycler);
|
||||
recycler.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
if (mPendingSavedState != null) {
|
||||
return new SavedState(mPendingSavedState);
|
||||
}
|
||||
SavedState savedState = new SavedState();
|
||||
savedState.position = mPendingScrollPosition;
|
||||
savedState.offset = mOffset;
|
||||
savedState.isReverseLayout = mShouldReverseLayout;
|
||||
return savedState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
if (state instanceof SavedState) {
|
||||
mPendingSavedState = new SavedState((SavedState) state);
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if {@link #getOrientation()} is {@link #HORIZONTAL}
|
||||
*/
|
||||
@Override
|
||||
public boolean canScrollHorizontally() {
|
||||
return mOrientation == HORIZONTAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if {@link #getOrientation()} is {@link #VERTICAL}
|
||||
*/
|
||||
@Override
|
||||
public boolean canScrollVertically() {
|
||||
return mOrientation == VERTICAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current orientation of the layout.
|
||||
*
|
||||
* @return Current orientation, either {@link #HORIZONTAL} or {@link #VERTICAL}
|
||||
* @see #setOrientation(int)
|
||||
*/
|
||||
public int getOrientation() {
|
||||
return mOrientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the orientation of the layout. {@link ViewPagerLayoutManager}
|
||||
* will do its best to keep scroll position.
|
||||
*
|
||||
* @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
|
||||
*/
|
||||
public void setOrientation(int orientation) {
|
||||
if (orientation != HORIZONTAL && orientation != VERTICAL) {
|
||||
throw new IllegalArgumentException("invalid orientation:" + orientation);
|
||||
}
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (orientation == mOrientation) {
|
||||
return;
|
||||
}
|
||||
mOrientation = orientation;
|
||||
mOrientationHelper = null;
|
||||
mDistanceToBottom = INVALID_SIZE;
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the max visible item count, {@link #DETERMINE_BY_MAX_AND_MIN} means it haven't been set now
|
||||
* And it will use {@link #maxRemoveOffset()} and {@link #minRemoveOffset()} to handle the range
|
||||
*
|
||||
* @return Max visible item count
|
||||
*/
|
||||
public int getMaxVisibleItemCount() {
|
||||
return mMaxVisibleItemCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the max visible item count, {@link #DETERMINE_BY_MAX_AND_MIN} means it haven't been set now
|
||||
* And it will use {@link #maxRemoveOffset()} and {@link #minRemoveOffset()} to handle the range
|
||||
*
|
||||
* @param mMaxVisibleItemCount Max visible item count
|
||||
*/
|
||||
public void setMaxVisibleItemCount(int mMaxVisibleItemCount) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.mMaxVisibleItemCount == mMaxVisibleItemCount) return;
|
||||
this.mMaxVisibleItemCount = mMaxVisibleItemCount;
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the view layout order. (e.g. from end to start or start to end)
|
||||
* RTL layout support is applied automatically. So if layout is RTL and
|
||||
* {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left.
|
||||
*/
|
||||
private void resolveShouldLayoutReverse() {
|
||||
// A == B is the same result, but we rather keep it readable
|
||||
if (mOrientation == VERTICAL || !isLayoutRTL()) {
|
||||
mShouldReverseLayout = mReverseLayout;
|
||||
} else {
|
||||
mShouldReverseLayout = !mReverseLayout;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if views are laid out from the opposite direction of the layout.
|
||||
*
|
||||
* @return If layout is reversed or not.
|
||||
* @see #setReverseLayout(boolean)
|
||||
*/
|
||||
public boolean getReverseLayout() {
|
||||
return mReverseLayout;
|
||||
}
|
||||
|
||||
public void setReverseLayout(boolean reverseLayout) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (reverseLayout == mReverseLayout) {
|
||||
return;
|
||||
}
|
||||
mReverseLayout = reverseLayout;
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
public void setSmoothScrollInterpolator(Interpolator smoothScrollInterpolator) {
|
||||
this.mSmoothScrollInterpolator = smoothScrollInterpolator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
|
||||
final int offsetPosition;
|
||||
|
||||
// fix wrong scroll direction when infinite enable
|
||||
if (mInfinite) {
|
||||
final int currentPosition = getCurrentPosition();
|
||||
final int total = getItemCount();
|
||||
final int targetPosition;
|
||||
if (position < currentPosition) {
|
||||
int d1 = currentPosition - position;
|
||||
int d2 = total - currentPosition + position;
|
||||
targetPosition = d1 < d2 ? (currentPosition - d1) : (currentPosition + d2);
|
||||
} else {
|
||||
int d1 = position - currentPosition;
|
||||
int d2 = currentPosition + total - position;
|
||||
targetPosition = d1 < d2 ? (currentPosition + d1) : (currentPosition - d2);
|
||||
}
|
||||
|
||||
offsetPosition = getOffsetToPosition(targetPosition);
|
||||
} else {
|
||||
offsetPosition = getOffsetToPosition(position);
|
||||
}
|
||||
|
||||
if (mOrientation == VERTICAL) {
|
||||
recyclerView.smoothScrollBy(0, offsetPosition, mSmoothScrollInterpolator);
|
||||
} else {
|
||||
recyclerView.smoothScrollBy(offsetPosition, 0, mSmoothScrollInterpolator);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||
if (state.getItemCount() == 0) {
|
||||
removeAndRecycleAllViews(recycler);
|
||||
mOffset = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
ensureLayoutState();
|
||||
resolveShouldLayoutReverse();
|
||||
|
||||
//make sure properties are correct while measure more than once
|
||||
View scrap = getMeasureView(recycler, state, 0);
|
||||
if (scrap == null) {
|
||||
removeAndRecycleAllViews(recycler);
|
||||
mOffset = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
measureChildWithMargins(scrap, 0, 0);
|
||||
mDecoratedMeasurement = mOrientationHelper.getDecoratedMeasurement(scrap);
|
||||
mDecoratedMeasurementInOther = mOrientationHelper.getDecoratedMeasurementInOther(scrap);
|
||||
mSpaceMain = (mOrientationHelper.getTotalSpace() - mDecoratedMeasurement) / 2;
|
||||
if (mDistanceToBottom == INVALID_SIZE) {
|
||||
mSpaceInOther = (mOrientationHelper.getTotalSpaceInOther() - mDecoratedMeasurementInOther) / 2;
|
||||
} else {
|
||||
mSpaceInOther = mOrientationHelper.getTotalSpaceInOther() - mDecoratedMeasurementInOther - mDistanceToBottom;
|
||||
}
|
||||
|
||||
mInterval = setInterval();
|
||||
setUp();
|
||||
if (mInterval == 0) {
|
||||
mLeftItems = 1;
|
||||
mRightItems = 1;
|
||||
} else {
|
||||
mLeftItems = (int) Math.abs(minRemoveOffset() / mInterval) + 1;
|
||||
mRightItems = (int) Math.abs(maxRemoveOffset() / mInterval) + 1;
|
||||
}
|
||||
|
||||
if (mPendingSavedState != null) {
|
||||
mShouldReverseLayout = mPendingSavedState.isReverseLayout;
|
||||
mPendingScrollPosition = mPendingSavedState.position;
|
||||
mOffset = mPendingSavedState.offset;
|
||||
}
|
||||
|
||||
if (mPendingScrollPosition != NO_POSITION) {
|
||||
mOffset = mShouldReverseLayout ?
|
||||
mPendingScrollPosition * -mInterval : mPendingScrollPosition * mInterval;
|
||||
}
|
||||
|
||||
layoutItems(recycler);
|
||||
}
|
||||
|
||||
private View getMeasureView(RecyclerView.Recycler recycler, RecyclerView.State state, int index) {
|
||||
if (index >= state.getItemCount() || index < 0) return null;
|
||||
try {
|
||||
return recycler.getViewForPosition(index);
|
||||
} catch (Exception e) {
|
||||
return getMeasureView(recycler, state, index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayoutCompleted(RecyclerView.State state) {
|
||||
super.onLayoutCompleted(state);
|
||||
mPendingSavedState = null;
|
||||
mPendingScrollPosition = NO_POSITION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views, int direction, int focusableMode) {
|
||||
final int currentPosition = getCurrentPosition();
|
||||
final View currentView = findViewByPosition(currentPosition);
|
||||
if (currentView == null) return true;
|
||||
if (recyclerView.hasFocus()) {
|
||||
final int movement = getMovement(direction);
|
||||
if (movement != DIRECTION_NO_WHERE) {
|
||||
final int targetPosition = movement == DIRECTION_BACKWARD ?
|
||||
currentPosition - 1 : currentPosition + 1;
|
||||
ScrollHelper.smoothScrollToPosition(recyclerView, this, targetPosition);
|
||||
}
|
||||
} else {
|
||||
currentView.addFocusables(views, direction, focusableMode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onFocusSearchFailed(View focused, int focusDirection, RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private int getMovement(int direction) {
|
||||
if (mOrientation == VERTICAL) {
|
||||
if (direction == View.FOCUS_UP) {
|
||||
return mShouldReverseLayout ? DIRECTION_FORWARD : DIRECTION_BACKWARD;
|
||||
} else if (direction == View.FOCUS_DOWN) {
|
||||
return mShouldReverseLayout ? DIRECTION_BACKWARD : DIRECTION_FORWARD;
|
||||
} else {
|
||||
return DIRECTION_NO_WHERE;
|
||||
}
|
||||
} else {
|
||||
if (direction == View.FOCUS_LEFT) {
|
||||
return mShouldReverseLayout ? DIRECTION_FORWARD : DIRECTION_BACKWARD;
|
||||
} else if (direction == View.FOCUS_RIGHT) {
|
||||
return mShouldReverseLayout ? DIRECTION_BACKWARD : DIRECTION_FORWARD;
|
||||
} else {
|
||||
return DIRECTION_NO_WHERE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ensureLayoutState() {
|
||||
if (mOrientationHelper == null) {
|
||||
mOrientationHelper = OrientationHelper.createOrientationHelper(this, mOrientation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* You can set up your own properties here or change the exist properties like mSpaceMain and mSpaceInOther
|
||||
*/
|
||||
protected void setUp() {
|
||||
|
||||
}
|
||||
|
||||
private float getProperty(int position) {
|
||||
return mShouldReverseLayout ? position * -mInterval : position * mInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) {
|
||||
removeAllViews();
|
||||
mOffset = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollToPosition(int position) {
|
||||
if (!mInfinite && (position < 0 || position >= getItemCount())) return;
|
||||
mPendingScrollPosition = position;
|
||||
mOffset = mShouldReverseLayout ? position * -mInterval : position * mInterval;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int computeHorizontalScrollOffset(RecyclerView.State state) {
|
||||
return computeScrollOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int computeVerticalScrollOffset(RecyclerView.State state) {
|
||||
return computeScrollOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int computeHorizontalScrollExtent(RecyclerView.State state) {
|
||||
return computeScrollExtent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int computeVerticalScrollExtent(RecyclerView.State state) {
|
||||
return computeScrollExtent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int computeHorizontalScrollRange(RecyclerView.State state) {
|
||||
return computeScrollRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int computeVerticalScrollRange(RecyclerView.State state) {
|
||||
return computeScrollRange();
|
||||
}
|
||||
|
||||
private int computeScrollOffset() {
|
||||
if (getChildCount() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!mSmoothScrollbarEnabled) {
|
||||
return !mShouldReverseLayout ?
|
||||
getCurrentPosition() : getItemCount() - getCurrentPosition() - 1;
|
||||
}
|
||||
|
||||
final float realOffset = getOffsetOfRightAdapterPosition();
|
||||
return !mShouldReverseLayout ? (int) realOffset : (int) ((getItemCount() - 1) * mInterval + realOffset);
|
||||
}
|
||||
|
||||
private int computeScrollExtent() {
|
||||
if (getChildCount() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!mSmoothScrollbarEnabled) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (int) mInterval;
|
||||
}
|
||||
|
||||
private int computeScrollRange() {
|
||||
if (getChildCount() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!mSmoothScrollbarEnabled) {
|
||||
return getItemCount();
|
||||
}
|
||||
|
||||
return (int) (getItemCount() * mInterval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||
if (mOrientation == VERTICAL) {
|
||||
return 0;
|
||||
}
|
||||
return scrollBy(dx, recycler, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||
if (mOrientation == HORIZONTAL) {
|
||||
return 0;
|
||||
}
|
||||
return scrollBy(dy, recycler, state);
|
||||
}
|
||||
|
||||
private int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||
if (getChildCount() == 0 || dy == 0) {
|
||||
return 0;
|
||||
}
|
||||
ensureLayoutState();
|
||||
int willScroll = dy;
|
||||
|
||||
float realDx = dy / getDistanceRatio();
|
||||
if (Math.abs(realDx) < 0.00000001f) {
|
||||
return 0;
|
||||
}
|
||||
float targetOffset = mOffset + realDx;
|
||||
|
||||
//handle the boundary
|
||||
if (!mInfinite && targetOffset < getMinOffset()) {
|
||||
willScroll -= (targetOffset - getMinOffset()) * getDistanceRatio();
|
||||
} else if (!mInfinite && targetOffset > getMaxOffset()) {
|
||||
willScroll = (int) ((getMaxOffset() - mOffset) * getDistanceRatio());
|
||||
}
|
||||
|
||||
realDx = willScroll / getDistanceRatio();
|
||||
|
||||
mOffset += realDx;
|
||||
|
||||
//handle recycle
|
||||
layoutItems(recycler);
|
||||
|
||||
return willScroll;
|
||||
}
|
||||
|
||||
private void layoutItems(RecyclerView.Recycler recycler) {
|
||||
detachAndScrapAttachedViews(recycler);
|
||||
positionCache.clear();
|
||||
|
||||
final int itemCount = getItemCount();
|
||||
if (itemCount == 0) return;
|
||||
|
||||
// make sure that current position start from 0 to 1
|
||||
final int currentPos = mShouldReverseLayout ?
|
||||
-getCurrentPositionOffset() : getCurrentPositionOffset();
|
||||
int start = currentPos - mLeftItems;
|
||||
int end = currentPos + mRightItems;
|
||||
|
||||
// handle max visible count
|
||||
if (useMaxVisibleCount()) {
|
||||
boolean isEven = mMaxVisibleItemCount % 2 == 0;
|
||||
if (isEven) {
|
||||
int offset = mMaxVisibleItemCount / 2;
|
||||
start = currentPos - offset + 1;
|
||||
end = currentPos + offset + 1;
|
||||
} else {
|
||||
int offset = (mMaxVisibleItemCount - 1) / 2;
|
||||
start = currentPos - offset;
|
||||
end = currentPos + offset + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mInfinite) {
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
if (useMaxVisibleCount()) end = mMaxVisibleItemCount;
|
||||
}
|
||||
if (end > itemCount) end = itemCount;
|
||||
}
|
||||
|
||||
float lastOrderWeight = Float.MIN_VALUE;
|
||||
for (int i = start; i < end; i++) {
|
||||
if (useMaxVisibleCount() || !removeCondition(getProperty(i) - mOffset)) {
|
||||
// start and end base on current position,
|
||||
// so we need to calculate the adapter position
|
||||
int adapterPosition = i;
|
||||
if (i >= itemCount) {
|
||||
adapterPosition %= itemCount;
|
||||
} else if (i < 0) {
|
||||
int delta = (-adapterPosition) % itemCount;
|
||||
if (delta == 0) delta = itemCount;
|
||||
adapterPosition = itemCount - delta;
|
||||
}
|
||||
final View scrap = recycler.getViewForPosition(adapterPosition);
|
||||
measureChildWithMargins(scrap, 0, 0);
|
||||
resetViewProperty(scrap);
|
||||
// we need i to calculate the real offset of current view
|
||||
final float targetOffset = getProperty(i) - mOffset;
|
||||
layoutScrap(scrap, targetOffset);
|
||||
final float orderWeight = mEnableBringCenterToFront ?
|
||||
setViewElevation(scrap, targetOffset) : adapterPosition;
|
||||
if (orderWeight > lastOrderWeight) {
|
||||
addView(scrap);
|
||||
} else {
|
||||
addView(scrap, 0);
|
||||
}
|
||||
if (i == currentPos) currentFocusView = scrap;
|
||||
lastOrderWeight = orderWeight;
|
||||
positionCache.put(i, scrap);
|
||||
}
|
||||
}
|
||||
|
||||
currentFocusView.requestFocus();
|
||||
}
|
||||
|
||||
private boolean useMaxVisibleCount() {
|
||||
return mMaxVisibleItemCount != DETERMINE_BY_MAX_AND_MIN;
|
||||
}
|
||||
|
||||
private boolean removeCondition(float targetOffset) {
|
||||
return targetOffset > maxRemoveOffset() || targetOffset < minRemoveOffset();
|
||||
}
|
||||
|
||||
private void resetViewProperty(View v) {
|
||||
v.setRotation(0);
|
||||
v.setRotationY(0);
|
||||
v.setRotationX(0);
|
||||
v.setScaleX(1f);
|
||||
v.setScaleY(1f);
|
||||
v.setAlpha(1f);
|
||||
}
|
||||
|
||||
/* package */ float getMaxOffset() {
|
||||
return !mShouldReverseLayout ? (getItemCount() - 1) * mInterval : 0;
|
||||
}
|
||||
|
||||
/* package */ float getMinOffset() {
|
||||
return !mShouldReverseLayout ? 0 : -(getItemCount() - 1) * mInterval;
|
||||
}
|
||||
|
||||
private void layoutScrap(View scrap, float targetOffset) {
|
||||
final int left = calItemLeft(scrap, targetOffset);
|
||||
final int top = calItemTop(scrap, targetOffset);
|
||||
if (mOrientation == VERTICAL) {
|
||||
layoutDecorated(scrap, mSpaceInOther + left, mSpaceMain + top,
|
||||
mSpaceInOther + left + mDecoratedMeasurementInOther, mSpaceMain + top + mDecoratedMeasurement);
|
||||
} else {
|
||||
layoutDecorated(scrap, mSpaceMain + left, mSpaceInOther + top,
|
||||
mSpaceMain + left + mDecoratedMeasurement, mSpaceInOther + top + mDecoratedMeasurementInOther);
|
||||
}
|
||||
setItemViewProperty(scrap, targetOffset);
|
||||
}
|
||||
|
||||
protected int calItemLeft(View itemView, float targetOffset) {
|
||||
return mOrientation == VERTICAL ? 0 : (int) targetOffset;
|
||||
}
|
||||
|
||||
protected int calItemTop(View itemView, float targetOffset) {
|
||||
return mOrientation == VERTICAL ? (int) targetOffset : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* when the target offset reach this,
|
||||
* the view will be removed and recycled in {@link #layoutItems(RecyclerView.Recycler)}
|
||||
*/
|
||||
protected float maxRemoveOffset() {
|
||||
return mOrientationHelper.getTotalSpace() - mSpaceMain;
|
||||
}
|
||||
|
||||
/**
|
||||
* when the target offset reach this,
|
||||
* the view will be removed and recycled in {@link #layoutItems(RecyclerView.Recycler)}
|
||||
*/
|
||||
protected float minRemoveOffset() {
|
||||
return -mDecoratedMeasurement - mOrientationHelper.getStartAfterPadding() - mSpaceMain;
|
||||
}
|
||||
|
||||
protected float getDistanceRatio() {
|
||||
return 1f;
|
||||
}
|
||||
|
||||
public int getCurrentPosition() {
|
||||
if (getItemCount() == 0) return 0;
|
||||
|
||||
int position = getCurrentPositionOffset();
|
||||
if (!mInfinite) return Math.abs(position);
|
||||
|
||||
position = !mShouldReverseLayout ?
|
||||
//take care of position = getItemCount()
|
||||
(position >= 0 ?
|
||||
position % getItemCount() :
|
||||
getItemCount() + position % getItemCount()) :
|
||||
(position > 0 ?
|
||||
getItemCount() - position % getItemCount() :
|
||||
-position % getItemCount());
|
||||
return position == getItemCount() ? 0 : position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View findViewByPosition(int position) {
|
||||
final int itemCount = getItemCount();
|
||||
if (itemCount == 0) return null;
|
||||
for (int i = 0; i < positionCache.size(); i++) {
|
||||
final int key = positionCache.keyAt(i);
|
||||
if (key >= 0) {
|
||||
if (position == key % itemCount) return positionCache.valueAt(i);
|
||||
} else {
|
||||
int delta = key % itemCount;
|
||||
if (delta == 0) delta = -itemCount;
|
||||
if (itemCount + delta == position) return positionCache.valueAt(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getLayoutPositionOfView(View v) {
|
||||
for (int i = 0; i < positionCache.size(); i++) {
|
||||
int key = positionCache.keyAt(i);
|
||||
View value = positionCache.get(key);
|
||||
if (value == v) return key;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* package */ int getCurrentPositionOffset() {
|
||||
if (mInterval == 0) return 0;
|
||||
return Math.round(mOffset / mInterval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sometimes we need to get the right offset of matching adapter position
|
||||
* cause when {@link #mInfinite} is set true, there will be no limitation of {@link #mOffset}
|
||||
*/
|
||||
private float getOffsetOfRightAdapterPosition() {
|
||||
if (mShouldReverseLayout)
|
||||
return mInfinite ?
|
||||
(mOffset <= 0 ?
|
||||
(mOffset % (mInterval * getItemCount())) :
|
||||
(getItemCount() * -mInterval + mOffset % (mInterval * getItemCount()))) :
|
||||
mOffset;
|
||||
else
|
||||
return mInfinite ?
|
||||
(mOffset >= 0 ?
|
||||
(mOffset % (mInterval * getItemCount())) :
|
||||
(getItemCount() * mInterval + mOffset % (mInterval * getItemCount()))) :
|
||||
mOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* used by {@link CenterSnapHelper} to center the current view
|
||||
*
|
||||
* @return the dy between center and current position
|
||||
*/
|
||||
public int getOffsetToCenter() {
|
||||
if (mInfinite)
|
||||
return (int) ((getCurrentPositionOffset() * mInterval - mOffset) * getDistanceRatio());
|
||||
return (int) ((getCurrentPosition() *
|
||||
(!mShouldReverseLayout ? mInterval : -mInterval) - mOffset) * getDistanceRatio());
|
||||
}
|
||||
|
||||
public int getOffsetToPosition(int position) {
|
||||
if (mInfinite)
|
||||
return (int) (((getCurrentPositionOffset() +
|
||||
(!mShouldReverseLayout ? position - getCurrentPositionOffset() : -getCurrentPositionOffset() - position)) *
|
||||
mInterval - mOffset) * getDistanceRatio());
|
||||
return (int) ((position *
|
||||
(!mShouldReverseLayout ? mInterval : -mInterval) - mOffset) * getDistanceRatio());
|
||||
}
|
||||
|
||||
public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
|
||||
this.onPageChangeListener = onPageChangeListener;
|
||||
}
|
||||
|
||||
public boolean getInfinite() {
|
||||
return mInfinite;
|
||||
}
|
||||
|
||||
public void setInfinite(boolean enable) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (enable == mInfinite) {
|
||||
return;
|
||||
}
|
||||
mInfinite = enable;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public int getDistanceToBottom() {
|
||||
return mDistanceToBottom == INVALID_SIZE ?
|
||||
(mOrientationHelper.getTotalSpaceInOther() - mDecoratedMeasurementInOther) / 2 : mDistanceToBottom;
|
||||
}
|
||||
|
||||
public void setDistanceToBottom(int mDistanceToBottom) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (this.mDistanceToBottom == mDistanceToBottom) return;
|
||||
this.mDistanceToBottom = mDistanceToBottom;
|
||||
removeAllViews();
|
||||
}
|
||||
|
||||
public boolean getEnableBringCenterToFront() {
|
||||
return mEnableBringCenterToFront;
|
||||
}
|
||||
|
||||
public void setEnableBringCenterToFront(boolean bringCenterToTop) {
|
||||
assertNotInLayoutOrScroll(null);
|
||||
if (mEnableBringCenterToFront == bringCenterToTop) {
|
||||
return;
|
||||
}
|
||||
this.mEnableBringCenterToFront = bringCenterToTop;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current state of the smooth scrollbar feature. It is enabled by default.
|
||||
*
|
||||
* @return True if smooth scrollbar is enabled, false otherwise.
|
||||
* @see #setSmoothScrollbarEnabled(boolean)
|
||||
*/
|
||||
public boolean getSmoothScrollbarEnabled() {
|
||||
return mSmoothScrollbarEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* When smooth scrollbar is enabled, the position and size of the scrollbar thumb is computed
|
||||
* based on the number of visible pixels in the visible items. This however assumes that all
|
||||
* list items have similar or equal widths or heights (depending on list orientation).
|
||||
* If you use a list in which items have different dimensions, the scrollbar will change
|
||||
* appearance as the user scrolls through the list. To avoid this issue, you need to disable
|
||||
* this property.
|
||||
* <p>
|
||||
* When smooth scrollbar is disabled, the position and size of the scrollbar thumb is based
|
||||
* solely on the number of items in the adapter and the position of the visible items inside
|
||||
* the adapter. This provides a stable scrollbar as the user navigates through a list of items
|
||||
* with varying widths / heights.
|
||||
*
|
||||
* @param enabled Whether or not to enable smooth scrollbar.
|
||||
* @see #setSmoothScrollbarEnabled(boolean)
|
||||
*/
|
||||
public void setSmoothScrollbarEnabled(boolean enabled) {
|
||||
mSmoothScrollbarEnabled = enabled;
|
||||
}
|
||||
|
||||
public interface OnPageChangeListener {
|
||||
void onPageSelected(int position);
|
||||
|
||||
void onPageScrollStateChanged(int state);
|
||||
}
|
||||
|
||||
private static class SavedState implements Parcelable {
|
||||
public static final Creator<SavedState> CREATOR
|
||||
= new Creator<SavedState>() {
|
||||
@Override
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
int position;
|
||||
float offset;
|
||||
boolean isReverseLayout;
|
||||
|
||||
SavedState() {
|
||||
|
||||
}
|
||||
|
||||
SavedState(Parcel in) {
|
||||
position = in.readInt();
|
||||
offset = in.readFloat();
|
||||
isReverseLayout = in.readInt() == 1;
|
||||
}
|
||||
|
||||
public SavedState(SavedState other) {
|
||||
position = other.position;
|
||||
offset = other.offset;
|
||||
isReverseLayout = other.isReverseLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(position);
|
||||
dest.writeFloat(offset);
|
||||
dest.writeInt(isReverseLayout ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|