Prima implementazione delle Ultime Consegne.

This commit is contained in:
2019-05-02 16:06:45 +02:00
parent 4aa8cef617
commit 82e21ccacb
53 changed files with 1451 additions and 408 deletions

View File

@@ -1,39 +0,0 @@
package it.integry.integrywmsnative.core.di.binders;
import androidx.databinding.BindingAdapter;
import androidx.databinding.ObservableArrayList;
public class RecyclerViewAdapterBinders {
// @BindingAdapter("app:adapter")
// public static void setItems(RecyclerView recyclerView, Class clazz) {
// setItems(recyclerView, null, clazz);
// }
//
// @BindingAdapter("app:items")
// public static <T> void setItems(RecyclerView recyclerView, ObservableArrayList<T> entries) {
// setItems(recyclerView, entries, null);
// }
//
// @BindingAdapter({"app:items", "app:adapter"})
// public static <T> void setItems(RecyclerView recyclerView,
// ObservableArrayList<T> entries, Class clazz) {
//
//// recyclerView.removeAllViews();
//// if (entries != null) {
//// LayoutInflater inflater = (LayoutInflater)
//// viewGroup.getContext()
//// .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//// for (int i = 0; i < entries.size(); i++) {
//// T entry = entries.get(i);
//// ViewDataBinding bindings = DataBindingUtil
//// .inflate(inflater, layoutId, viewGroup, true);
//// bindings.setVariable(BR.data, entry);
//// }
//// }
// }
}

View File

@@ -16,6 +16,7 @@ public class UtilityDate {
public static final String YMD_TIME_SLASH = YMD_SLASH + " hh:mm";
public static final String DM_HUMAN = "dd MMM";
public static final String DMY_HUMAN = "dd MMM yyyy";
public static final String DMY_HUMAN_LONG = "dd MMMM yyyy";
}

View File

@@ -217,10 +217,10 @@ public class MainFragment extends Fragment implements ITitledFragment, IScrollab
((MainActivity) getActivity()).setItem(R.id.nav_free_picking);
}
// @OnClick(R.id.fast_button_resi_clienti)
// public void onClickResiClientio(View view) {
// ((MainActivity) getActivity()).setItem(R.id.nav_resi_cliente);
// }
@OnClick(R.id.fast_button_resi_clienti)
public void onClickResiClientio(View view) {
((MainActivity) getActivity()).setItem(R.id.nav_resi_cliente);
}
@Override
public void setScrollToolbar(ElevatedToolbar toolbar) {

View File

@@ -1,7 +1,6 @@
package it.integry.integrywmsnative.gest.ultime_consegne_cliente;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
@@ -12,16 +11,16 @@ import androidx.appcompat.widget.AppCompatTextView;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import it.integry.integrywmsnative.R;
import it.integry.integrywmsnative.core.interfaces.IScrollableFragment;
import it.integry.integrywmsnative.core.interfaces.ITitledFragment;
import it.integry.integrywmsnative.core.settings.SettingsManager;
import it.integry.integrywmsnative.core.utility.UtilityExceptions;
import it.integry.integrywmsnative.core.utility.UtilityProgress;
import it.integry.integrywmsnative.databinding.FragmentMainUltimeConsegneClienteBinding;
import it.integry.integrywmsnative.gest.ultime_consegne_cliente.viewmodel.UltimeConsegneClienteViewModel;
import it.integry.integrywmsnative.gest.vendita.rest.UltimeConsegneClienteRESTConsumer;
import it.integry.integrywmsnative.ui.ElevatedToolbar;
public class UltimeConsegneClienteFragment extends Fragment implements ITitledFragment {
public class UltimeConsegneClienteFragment extends Fragment implements ITitledFragment, IScrollableFragment {
private Runnable mOnPreDestroy;
private ElevatedToolbar mToolbar;
public UltimeConsegneClienteFragment() {
// Required empty public constructor
@@ -42,14 +41,32 @@ public class UltimeConsegneClienteFragment extends Fragment implements ITitledFr
FragmentMainUltimeConsegneClienteBinding mBinding = DataBindingUtil.inflate(LayoutInflater.from(getActivity()), R.layout.fragment_main_ultime_consegne_cliente, container, false);
mBinding.setViewmodel(new UltimeConsegneClienteViewModel(getActivity()));
mBinding.setViewmodel(new UltimeConsegneClienteViewModel(getActivity(), mBinding));
mToolbar.setRecyclerView(mBinding.recyclerView);
// Inflate the layout for this fragment
return mBinding.getRoot();
}
@Override
public void onDestroy() {
if(mOnPreDestroy != null) mOnPreDestroy.run();
super.onDestroy();
}
@Override
public void onCreateActionBar(AppCompatTextView titleText, Context context) {
titleText.setText(context.getText(R.string.fragment_ultime_consegne_cliente_title).toString());
}
@Override
public void setScrollToolbar(ElevatedToolbar toolbar) {
mToolbar = toolbar;
}
@Override
public void setOnPreDestroy(Runnable onPreDestroy) {
mOnPreDestroy = onPreDestroy;
}
}

View File

@@ -0,0 +1,48 @@
package it.integry.integrywmsnative.gest.ultime_consegne_cliente.dto;
import androidx.databinding.Observable;
import it.integry.integrywmsnative.core.di.BindableBoolean;
import it.integry.integrywmsnative.gest.vendita.rest.model.ConsegnaClienteDTO;
public class CheckableConsegnaClienteDTO {
private ConsegnaClienteDTO mItem;
private BindableBoolean checked = new BindableBoolean(false);
public CheckableConsegnaClienteDTO(ConsegnaClienteDTO item) {
this.mItem = item;
}
public ConsegnaClienteDTO getItem() {
return mItem;
}
public CheckableConsegnaClienteDTO setItem(ConsegnaClienteDTO item) {
this.mItem = item;
return this;
}
public BindableBoolean getChecked() {
return checked;
}
public boolean isChecked() {
return checked.get();
}
public CheckableConsegnaClienteDTO setChecked(BindableBoolean checked) {
this.checked = checked;
return this;
}
public CheckableConsegnaClienteDTO setCheckedValue(boolean value) {
this.checked.set(value);
return this;
}
public void toggleCheck() {
this.checked.set(!this.checked.get());
}
}

View File

@@ -1,4 +1,4 @@
package it.integry.integrywmsnative.gest.vendita.rest;
package it.integry.integrywmsnative.gest.ultime_consegne_cliente.rest;
import com.google.gson.reflect.TypeToken;
@@ -15,7 +15,7 @@ import it.integry.integrywmsnative.gest.vendita.rest.model.ConsegnaClienteDTO;
public class UltimeConsegneClienteRESTConsumer {
public static void getUltimeConsegneClienti(String codMdep, String codAnag, String codMart, RunnableArgs<List<ConsegnaClienteDTO>> onComplete, RunnableArgs<Exception> onFailed) {
public static void getUltimeConsegneClienti(String codMdep, String codAnag, String codMart, RunnableArgs<ArrayList<ConsegnaClienteDTO>> onComplete, RunnableArgs<Exception> onFailed) {
int numberOfConsegnePerCli = 10;
int numberOfDayToAnalyze = 90;
@@ -29,14 +29,14 @@ public class UltimeConsegneClienteRESTConsumer {
UtilityDB.valueToString(codMdep) + ", " +
UtilityDB.valueToString(numberOfDayToAnalyze) + ") consegne " +
"LEFT OUTER JOIN gtb_anag ON consegne.cod_anag = gtb_anag.cod_anag " +
"ORDER BY consegne.cod_anag, " +
"ORDER BY rag_soc, " +
" counter_consegna ";
Type typeOfObjectsList = new TypeToken<ArrayList<ConsegnaClienteDTO>>() {}.getType();
SystemRESTConsumer.processSql(sql, typeOfObjectsList, new ISimpleOperationCallback<List<ConsegnaClienteDTO>>() {
SystemRESTConsumer.processSql(sql, typeOfObjectsList, new ISimpleOperationCallback<ArrayList<ConsegnaClienteDTO>>() {
@Override
public void onSuccess(List<ConsegnaClienteDTO> value) {
public void onSuccess(ArrayList<ConsegnaClienteDTO> value) {
if(onComplete != null) onComplete.run(value);
}

View File

@@ -2,24 +2,36 @@ package it.integry.integrywmsnative.gest.ultime_consegne_cliente.viewmodel;
import android.app.ProgressDialog;
import android.content.Context;
import android.widget.Toast;
import androidx.databinding.ObservableArrayList;
import androidx.recyclerview.widget.LinearLayoutManager;
import java.util.ArrayList;
import java.util.List;
import it.integry.integrywmsnative.core.settings.SettingsManager;
import it.integry.integrywmsnative.core.utility.UtilityExceptions;
import it.integry.integrywmsnative.core.utility.UtilityProgress;
import it.integry.integrywmsnative.gest.vendita.rest.UltimeConsegneClienteRESTConsumer;
import it.integry.integrywmsnative.databinding.FragmentMainUltimeConsegneClienteBinding;
import it.integry.integrywmsnative.gest.ultime_consegne_cliente.rest.UltimeConsegneClienteRESTConsumer;
import it.integry.integrywmsnative.gest.vendita.rest.model.ConsegnaClienteDTO;
public class UltimeConsegneClienteViewModel {
private Context mContext;
private FragmentMainUltimeConsegneClienteBinding mBinding;
public UltimeConsegneClienteViewModel(Context context) {
public UltimeConsegneClienteViewModel(Context context, FragmentMainUltimeConsegneClienteBinding binding) {
this.mContext = context;
this.mBinding = binding;
this.initList();
}
private void initList() {
ProgressDialog progressDialog = UtilityProgress.createDefaultProgressDialog(mContext);
UltimeConsegneClienteRESTConsumer.getUltimeConsegneClienti(
@@ -27,10 +39,29 @@ public class UltimeConsegneClienteViewModel {
null,
null,
consegne -> {
this.initDataAdapter(consegne);
progressDialog.dismiss();
}, ex -> {
UtilityExceptions.defaultException(mContext, ex, progressDialog);
});
}
private void initDataAdapter(ArrayList<ConsegnaClienteDTO> dataset) {
UltimeConsegneMainListAdapter adapter = new UltimeConsegneMainListAdapter(mContext, dataset);
adapter.setOnItemClickListener(consegna -> {
Toast.makeText(mContext, String.format("Selezionato doc n° %d del %s", consegna.getNumDoc(), consegna.getDataDoc()), Toast.LENGTH_SHORT).show();
});
mBinding.recyclerView.setHasFixedSize(true);
mBinding.recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
mBinding.recyclerView.setAdapter(adapter);
mBinding.fastscroll.setRecyclerView(mBinding.recyclerView);
}
}

View File

@@ -0,0 +1,210 @@
package it.integry.integrywmsnative.gest.ultime_consegne_cliente.viewmodel;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.core.content.ContextCompat;
import androidx.core.content.res.ResourcesCompat;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.amulyakhare.textdrawable.TextDrawable;
import com.annimon.stream.Stream;
import com.zhukic.sectionedrecyclerview.SectionedRecyclerViewAdapter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import it.integry.integrywmsnative.R;
import it.integry.integrywmsnative.core.utility.UtilityDate;
import it.integry.integrywmsnative.core.utility.UtilityExceptions;
import it.integry.integrywmsnative.core.utility.UtilityString;
import it.integry.integrywmsnative.databinding.FragmentMainUltimeConsegneClienteListHeaderBinding;
import it.integry.integrywmsnative.databinding.FragmentMainUltimeConsegneClienteListSingleItemBinding;
import it.integry.integrywmsnative.gest.ultime_consegne_cliente.dto.CheckableConsegnaClienteDTO;
import it.integry.integrywmsnative.gest.vendita.rest.model.ConsegnaClienteDTO;
import it.integry.integrywmsnative.ui.fastscroll.SectionTitleProvider;
public class UltimeConsegneMainListAdapter extends SectionedRecyclerViewAdapter<UltimeConsegneMainListAdapter.SubheaderHolder, UltimeConsegneMainListAdapter.SingleItemViewHolder> implements SectionTitleProvider {
private Context mContext;
private ArrayList<ConsegnaClienteDTO> mDataset;
private ArrayList<String> mSectionTitleItems;
private OnItemClickListener onItemClickListener;
private TextDrawable.IShapeBuilder smallIconBuilder;
private TextDrawable.IShapeBuilder mediumIconBuilder;
private TextDrawable.IShapeBuilder largeIconBuilder;
@Override
public String getSectionTitle(int position) {
return this.mSectionTitleItems.get(position);
}
public interface OnItemClickListener {
void onItemClicked(ConsegnaClienteDTO consegna);
}
static class SubheaderHolder extends RecyclerView.ViewHolder {
FragmentMainUltimeConsegneClienteListHeaderBinding mBinding;
SubheaderHolder(FragmentMainUltimeConsegneClienteListHeaderBinding binding) {
super(binding.getRoot());
this.mBinding = binding;
}
}
static class SingleItemViewHolder extends RecyclerView.ViewHolder {
FragmentMainUltimeConsegneClienteListSingleItemBinding mBinding;
SingleItemViewHolder(FragmentMainUltimeConsegneClienteListSingleItemBinding binding) {
super(binding.getRoot());
this.mBinding = binding;
}
}
public UltimeConsegneMainListAdapter(Context context, ArrayList<ConsegnaClienteDTO> dataset) {
super();
this.mContext = context;
this.mDataset = new ArrayList<>();
this.mSectionTitleItems = new ArrayList<>();
Stream.of(dataset)
.map(x -> x.getRagSoc())
.distinct()
.forEach(x -> {
this.mSectionTitleItems.add(String.valueOf(x.charAt(0)));
Stream.of(dataset)
.filter(y -> y.getRagSoc().equalsIgnoreCase(x))
.forEach(y -> {
this.mSectionTitleItems.add(String.valueOf(x.charAt(0)));
});
});
this.mDataset = dataset;
smallIconBuilder = TextDrawable.builder()
.beginConfig()
.width(40)
.height(40)
.fontSize(24)
.useFont(ResourcesCompat.getFont(mContext, R.font.product_sans_regular))
.endConfig();
mediumIconBuilder = TextDrawable.builder()
.beginConfig()
.width(40)
.height(40)
.fontSize(20)
.useFont(ResourcesCompat.getFont(mContext, R.font.product_sans_regular))
.endConfig();
largeIconBuilder = TextDrawable.builder()
.beginConfig()
.width(40)
.height(40)
.fontSize(16)
.useFont(ResourcesCompat.getFont(mContext, R.font.product_sans_regular))
.endConfig();
}
@Override
public SingleItemViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
FragmentMainUltimeConsegneClienteListSingleItemBinding binding = DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout.fragment_main_ultime_consegne_cliente__list_single_item, parent, false);
return new SingleItemViewHolder(binding);
}
@Override
public SubheaderHolder onCreateSubheaderViewHolder(ViewGroup parent, int viewType) {
FragmentMainUltimeConsegneClienteListHeaderBinding binding = DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout.fragment_main_ultime_consegne_cliente__list_header, parent, false);
return new SubheaderHolder(binding);
}
@Override
public void onBindItemViewHolder(final SingleItemViewHolder holder, final int position) {
final ConsegnaClienteDTO consegna = this.mDataset.get(position);
holder.mBinding.descriptionMain.setText(UtilityString.isNullOrEmpty(consegna.getRifOrd()) ? "" : consegna.getRifOrd());
String numDoc = "" + consegna.getNumDoc();
if(numDoc.length() <= 2) {
holder.mBinding.startIcon.setImageDrawable(smallIconBuilder.buildRound(numDoc, ContextCompat.getColor(mContext, R.color.colorPrimary)));
} else if(numDoc.length() == 3) {
holder.mBinding.startIcon.setImageDrawable(mediumIconBuilder.buildRound(numDoc, ContextCompat.getColor(mContext, R.color.colorPrimary)));
} else if(numDoc.length() == 4) {
holder.mBinding.startIcon.setImageDrawable(largeIconBuilder.buildRound(numDoc, ContextCompat.getColor(mContext, R.color.colorPrimary)));
}
try {
Date dataDoc = UtilityDate.recognizeDate(consegna.getDataDoc());
Calendar calendarNow = Calendar.getInstance(TimeZone.getDefault());
Calendar calendarDataDoc = Calendar.getInstance(TimeZone.getDefault());
calendarDataDoc.setTime(dataDoc);
String dataDocString = "";
if(calendarDataDoc.get(Calendar.YEAR) == calendarNow.get(Calendar.YEAR)) {
dataDocString = UtilityDate.formatDate(dataDoc, UtilityDate.COMMONS_DATE_FORMATS.DM_HUMAN);
} else {
dataDocString = UtilityDate.formatDate(dataDoc, UtilityDate.COMMONS_DATE_FORMATS.DMY_HUMAN);
}
holder.mBinding.date.setText(dataDocString);
holder.mBinding.compilatoDa.setText(consegna.getCompilatoDa());
holder.mBinding.compilatoDa.setVisibility(UtilityString.isNullOrEmpty(consegna.getCompilatoDa()) ? View.GONE : View.VISIBLE);
} catch (Exception ex) {
UtilityExceptions.defaultException(mContext, ex);
}
holder.mBinding.getRoot().setOnClickListener(l -> {
if(onItemClickListener != null) onItemClickListener.onItemClicked(consegna);
});
}
@Override
public void onBindSubheaderViewHolder(SubheaderHolder subheaderHolder, int nextItemPosition) {
subheaderHolder.mBinding.title.setText(this.mDataset.get(nextItemPosition).getRagSoc());
}
@Override
public boolean onPlaceSubheaderBetweenItems(int position) {
if(!this.mDataset.get(position).getCodAnag().equalsIgnoreCase(this.mDataset.get(position+1).getCodAnag())) {
return true;
}
return false;
}
@Override
public int getItemSize() {
return this.mDataset.size();
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
}

View File

@@ -9,6 +9,9 @@ public class ConsegnaClienteDTO {
private String serDoc;
private int numDoc;
private String ragSoc;
private String compilatoDa;
private String rifOrd;
private String dataord;
public String getCodAnag() {
return codAnag;
@@ -72,4 +75,31 @@ public class ConsegnaClienteDTO {
this.ragSoc = ragSoc;
return this;
}
public String getCompilatoDa() {
return compilatoDa;
}
public ConsegnaClienteDTO setCompilatoDa(String compilatoDa) {
this.compilatoDa = compilatoDa;
return this;
}
public String getRifOrd() {
return rifOrd;
}
public ConsegnaClienteDTO setRifOrd(String rifOrd) {
this.rifOrd = rifOrd;
return this;
}
public String getDataord() {
return dataord;
}
public ConsegnaClienteDTO setDataord(String dataord) {
this.dataord = dataord;
return this;
}
}

View File

@@ -0,0 +1,294 @@
package it.integry.integrywmsnative.ui.fastscroll;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.widget.TextViewCompat;
import androidx.recyclerview.widget.RecyclerView;
import it.integry.integrywmsnative.R;
import it.integry.integrywmsnative.ui.fastscroll.viewprovider.DefaultScrollerViewProvider;
import it.integry.integrywmsnative.ui.fastscroll.viewprovider.ScrollerViewProvider;
/**
* Created by mklimczak on 28/07/15.
*/
public class FastScroller extends LinearLayout {
private static final int STYLE_NONE = -1;
private final RecyclerViewScrollListener scrollListener = new RecyclerViewScrollListener(this);
private RecyclerView recyclerView;
private View bubble;
private View handle;
private TextView bubbleTextView;
private int bubbleOffset;
private int handleColor;
private int bubbleColor;
private int bubbleTextAppearance;
private int scrollerOrientation;
//TODO the name should be fixed, also check if there is a better way of handling the visibility, because this is somewhat convoluted
private int maxVisibility;
private boolean manuallyChangingPosition;
private ScrollerViewProvider viewProvider;
private SectionTitleProvider titleProvider;
public FastScroller(Context context) {
this(context, null);
}
public FastScroller(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FastScroller(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setClipChildren(false);
TypedArray style = context.obtainStyledAttributes(attrs, R.styleable.fastscroll__fastScroller, R.attr.fastscroll__style, 0);
try {
bubbleColor = style.getColor(R.styleable.fastscroll__fastScroller_fastscroll__bubbleColor, STYLE_NONE);
handleColor = style.getColor(R.styleable.fastscroll__fastScroller_fastscroll__handleColor, STYLE_NONE);
bubbleTextAppearance = style.getResourceId(R.styleable.fastscroll__fastScroller_fastscroll__bubbleTextAppearance, STYLE_NONE);
} finally {
style.recycle();
}
maxVisibility = getVisibility();
setViewProvider(new DefaultScrollerViewProvider());
}
/**
* Enables custom layout for {@link FastScroller}.
* @param viewProvider A {@link ScrollerViewProvider} for the {@link FastScroller} to use when building layout.
*/
public void setViewProvider(ScrollerViewProvider viewProvider) {
removeAllViews();
this.viewProvider = viewProvider;
viewProvider.setFastScroller(this);
bubble = viewProvider.provideBubbleView(this);
handle = viewProvider.provideHandleView(this);
bubbleTextView = viewProvider.provideBubbleTextView();
addView(bubble);
addView(handle);
}
/**
* Attach the {@link FastScroller} to {@link RecyclerView}. Should be used after the adapter is set
* to the {@link RecyclerView}. If the adapter implements SectionTitleProvider, the FastScroller
* will show a bubble with title.
* @param recyclerView A {@link RecyclerView} to attach the {@link FastScroller} to.
*/
public void setRecyclerView(RecyclerView recyclerView) {
this.recyclerView = recyclerView;
if(recyclerView.getAdapter() instanceof SectionTitleProvider) titleProvider = (SectionTitleProvider) recyclerView.getAdapter();
recyclerView.addOnScrollListener(scrollListener);
invalidateVisibility();
recyclerView.setOnHierarchyChangeListener(new OnHierarchyChangeListener() {
@Override
public void onChildViewAdded(View parent, View child) {
invalidateVisibility();
}
@Override
public void onChildViewRemoved(View parent, View child) {
invalidateVisibility();
}
});
}
/**
* Set the orientation of the {@link FastScroller}. The orientation of the {@link FastScroller}
* should generally match the orientation of connected {@link RecyclerView} for good UX but it's not enforced.
* Note: This method is overridden from {@link LinearLayout#setOrientation(int)} but for {@link FastScroller}
* it has a totally different meaning.
* @param orientation of the {@link FastScroller}. {@link #VERTICAL} or {@link #HORIZONTAL}
*/
@Override
public void setOrientation(int orientation) {
scrollerOrientation = orientation;
//switching orientation, because orientation in linear layout
//is something different than orientation of fast scroller
super.setOrientation(orientation == HORIZONTAL ? VERTICAL : HORIZONTAL);
}
/**
* Set the background color of the bubble.
* @param color Color in hex notation with alpha channel, e.g. 0xFFFFFFFF
*/
public void setBubbleColor(int color) {
bubbleColor = color;
invalidate();
}
/**
* Set the background color of the handle.
* @param color Color in hex notation with alpha channel, e.g. 0xFFFFFFFF
*/
public void setHandleColor(int color) {
handleColor = color;
invalidate();
}
/**
* Sets the text appearance of the bubble.
* @param textAppearanceResourceId The id of the resource to be used as text appearance of the bubble.
*/
public void setBubbleTextAppearance(int textAppearanceResourceId){
bubbleTextAppearance = textAppearanceResourceId;
invalidate();
}
/**
* Add a {@link it.integry.integrywmsnative.ui.fastscroll.RecyclerViewScrollListener.ScrollerListener}
* to be notified of user scrolling
* @param listener
*/
public void addScrollerListener(RecyclerViewScrollListener.ScrollerListener listener){
scrollListener.addScrollerListener(listener);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
initHandleMovement();
bubbleOffset = viewProvider.getBubbleOffset();
applyStyling(); //TODO this doesn't belong here, even if it works
if (!isInEditMode()) {
//sometimes recycler starts with a defined scroll (e.g. when coming from saved state)
scrollListener.updateHandlePosition(recyclerView);
}
}
private void applyStyling() {
if(bubbleColor!=STYLE_NONE) setBackgroundTint(bubbleTextView, bubbleColor);
if(handleColor!=STYLE_NONE) setBackgroundTint(handle, handleColor);
if(bubbleTextAppearance!=STYLE_NONE) TextViewCompat.setTextAppearance(bubbleTextView, bubbleTextAppearance);
}
private void setBackgroundTint(View view, int color) {
final Drawable background = DrawableCompat.wrap(view.getBackground());
if(background==null) return;
DrawableCompat.setTint(background.mutate(), color);
Utils.setBackground(view, background);
}
private void initHandleMovement() {
handle.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
requestDisallowInterceptTouchEvent(true);
if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {
if(titleProvider!=null && event.getAction() == MotionEvent.ACTION_DOWN) viewProvider.onHandleGrabbed();
manuallyChangingPosition = true;
float relativePos = getRelativeTouchPosition(event);
setScrollerPosition(relativePos);
setRecyclerViewPosition(relativePos);
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
manuallyChangingPosition = false;
if(titleProvider!=null) viewProvider.onHandleReleased();
return true;
}
return false;
}
});
}
private float getRelativeTouchPosition(MotionEvent event){
if(isVertical()){
float yInParent = event.getRawY() - Utils.getViewRawY(handle);
return yInParent / (getHeight() - handle.getHeight());
} else {
float xInParent = event.getRawX() - Utils.getViewRawX(handle);
return xInParent / (getWidth() - handle.getWidth());
}
}
@Override
public void setVisibility(int visibility) {
maxVisibility = visibility;
invalidateVisibility();
}
private void invalidateVisibility() {
if(
recyclerView.getAdapter()==null ||
recyclerView.getAdapter().getItemCount()==0 ||
recyclerView.getChildAt(0)==null ||
isRecyclerViewNotScrollable() ||
maxVisibility != View.VISIBLE
){
super.setVisibility(INVISIBLE);
} else {
super.setVisibility(VISIBLE);
}
}
private boolean isRecyclerViewNotScrollable() {
if(isVertical()) {
return recyclerView.getChildAt(0).getHeight() * recyclerView.getAdapter().getItemCount() <= recyclerView.getHeight();
} else {
return recyclerView.getChildAt(0).getWidth() * recyclerView.getAdapter().getItemCount() <= recyclerView.getWidth();
}
}
private void setRecyclerViewPosition(float relativePos) {
if (recyclerView == null) return;
int itemCount = recyclerView.getAdapter().getItemCount();
int targetPos = (int) Utils.getValueInRange(0, itemCount - 1, (int) (relativePos * (float) itemCount));
recyclerView.scrollToPosition(targetPos);
if(titleProvider!=null && bubbleTextView!=null) bubbleTextView.setText(titleProvider.getSectionTitle(targetPos));
}
void setScrollerPosition(float relativePos) {
if(isVertical()) {
bubble.setY(Utils.getValueInRange(
0,
getHeight() - bubble.getHeight(),
relativePos * (getHeight() - handle.getHeight()) + bubbleOffset)
);
handle.setY(Utils.getValueInRange(
0,
getHeight() - handle.getHeight(),
relativePos * (getHeight() - handle.getHeight()))
);
} else {
bubble.setX(Utils.getValueInRange(
0,
getWidth() - bubble.getWidth(),
relativePos * (getWidth() - handle.getWidth()) + bubbleOffset)
);
handle.setX(Utils.getValueInRange(
0,
getWidth() - handle.getWidth(),
relativePos * (getWidth() - handle.getWidth()))
);
}
}
public boolean isVertical(){
return scrollerOrientation == VERTICAL;
}
boolean shouldUpdateHandlePosition() {
return handle!=null && !manuallyChangingPosition && recyclerView.getChildCount() > 0;
}
ScrollerViewProvider getViewProvider() {
return viewProvider;
}
}

View File

@@ -0,0 +1,71 @@
package it.integry.integrywmsnative.ui.fastscroll;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Michal on 04/08/16.
* Responsible for updating the handle / bubble position when user scrolls the {@link android.support.v7.widget.RecyclerView}.
*/
public class RecyclerViewScrollListener extends RecyclerView.OnScrollListener {
private final FastScroller scroller;
List<ScrollerListener> listeners = new ArrayList<>();
int oldScrollState = RecyclerView.SCROLL_STATE_IDLE;
public RecyclerViewScrollListener(FastScroller scroller) {
this.scroller = scroller;
}
public void addScrollerListener(ScrollerListener listener){
listeners.add(listener);
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newScrollState) {
super.onScrollStateChanged(recyclerView, newScrollState);
if(newScrollState==RecyclerView.SCROLL_STATE_IDLE && oldScrollState!=RecyclerView.SCROLL_STATE_IDLE){
scroller.getViewProvider().onScrollFinished();
} else if(newScrollState!=RecyclerView.SCROLL_STATE_IDLE && oldScrollState==RecyclerView.SCROLL_STATE_IDLE){
scroller.getViewProvider().onScrollStarted();
}
oldScrollState = newScrollState;
}
@Override
public void onScrolled(RecyclerView rv, int dx, int dy) {
if(scroller.shouldUpdateHandlePosition()) {
updateHandlePosition(rv);
}
}
void updateHandlePosition(RecyclerView rv) {
float relativePos;
if(rv != null) {
if (scroller.isVertical()) {
int offset = rv.computeVerticalScrollOffset();
int extent = rv.computeVerticalScrollExtent();
int range = rv.computeVerticalScrollRange();
relativePos = offset / (float) (range - extent);
} else {
int offset = rv.computeHorizontalScrollOffset();
int extent = rv.computeHorizontalScrollExtent();
int range = rv.computeHorizontalScrollRange();
relativePos = offset / (float) (range - extent);
}
scroller.setScrollerPosition(relativePos);
notifyListeners(relativePos);
}
}
public void notifyListeners(float relativePos){
for(ScrollerListener listener : listeners) listener.onScroll(relativePos);
}
public interface ScrollerListener {
void onScroll(float relativePos);
}
}

View File

@@ -0,0 +1,17 @@
package it.integry.integrywmsnative.ui.fastscroll;
/**
* Created by mklimczak on 31/07/15.
*/
public interface SectionTitleProvider{
/**
* Should be implemented by the adapter of the RecyclerView.
* Provides a text to be shown by the bubble, when RecyclerView reaches
* the position. Usually the first letter of the text shown by the item
* at this position.
* @param position Position of the row in adapter
* @return The text to be shown in the bubble
*/
String getSectionTitle(int position);
}

View File

@@ -0,0 +1,41 @@
package it.integry.integrywmsnative.ui.fastscroll;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.View;
/**
* Created by mklimczak on 31/07/15.
*/
public class Utils {
public static float getViewRawY(View view) {
int[] location = new int[2];
location[0] = 0;
location[1] = (int) view.getY();
((View)view.getParent()).getLocationInWindow(location);
return location[1];
}
public static float getViewRawX(View view) {
int[] location = new int[2];
location[0] = (int) view.getX();
location[1] = 0;
((View)view.getParent()).getLocationInWindow(location);
return location[0];
}
public static float getValueInRange(float min, float max, float value) {
float minimum = Math.max(min, value);
return Math.min(minimum, max);
}
public static void setBackground(View view, Drawable drawable){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
view.setBackground(drawable);
} else {
view.setBackgroundDrawable(drawable);
}
}
}

View File

@@ -0,0 +1,34 @@
package it.integry.integrywmsnative.ui.fastscroll.viewprovider;
/**
* Created by Michal on 11/08/16.
*/
public class DefaultBubbleBehavior implements ViewBehavior {
private final VisibilityAnimationManager animationManager;
public DefaultBubbleBehavior(VisibilityAnimationManager animationManager) {
this.animationManager = animationManager;
}
@Override
public void onHandleGrabbed() {
animationManager.show();
}
@Override
public void onHandleReleased() {
animationManager.hide();
}
@Override
public void onScrollStarted() {
}
@Override
public void onScrollFinished() {
}
}

View File

@@ -0,0 +1,66 @@
package it.integry.integrywmsnative.ui.fastscroll.viewprovider;
import android.graphics.drawable.InsetDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import it.integry.integrywmsnative.R;
import it.integry.integrywmsnative.ui.fastscroll.Utils;
/**
* Created by Michal on 05/08/16.
*/
public class DefaultScrollerViewProvider extends ScrollerViewProvider {
protected View bubble;
protected View handle;
@Override
public View provideHandleView(ViewGroup container) {
handle = new View(getContext());
int verticalInset = getScroller().isVertical() ? 0 : getContext().getResources().getDimensionPixelSize(R.dimen.fastscroll__handle_inset);
int horizontalInset = !getScroller().isVertical() ? 0 : getContext().getResources().getDimensionPixelSize(R.dimen.fastscroll__handle_inset);
InsetDrawable handleBg = new InsetDrawable(ContextCompat.getDrawable(getContext(), R.drawable.fastscroll__default_handle), horizontalInset, verticalInset, horizontalInset, verticalInset);
Utils.setBackground(handle, handleBg);
int handleWidth = getContext().getResources().getDimensionPixelSize(getScroller().isVertical() ? R.dimen.fastscroll__handle_clickable_width : R.dimen.fastscroll__handle_height);
int handleHeight = getContext().getResources().getDimensionPixelSize(getScroller().isVertical() ? R.dimen.fastscroll__handle_height : R.dimen.fastscroll__handle_clickable_width);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(handleWidth, handleHeight);
handle.setLayoutParams(params);
return handle;
}
@Override
public View provideBubbleView(ViewGroup container) {
bubble = LayoutInflater.from(getContext()).inflate(R.layout.fastscroll__default_bubble, container, false);
return bubble;
}
@Override
public TextView provideBubbleTextView() {
return (TextView) bubble;
}
@Override
public int getBubbleOffset() {
return (int) (getScroller().isVertical() ? ((float)handle.getHeight()/2f)-bubble.getHeight() : ((float)handle.getWidth()/2f)-bubble.getWidth());
}
@Override
protected ViewBehavior provideHandleBehavior() {
return null;
}
@Override
protected ViewBehavior provideBubbleBehavior() {
return new DefaultBubbleBehavior(new VisibilityAnimationManager.Builder(bubble).withPivotX(1f).withPivotY(1f).build());
}
}

View File

@@ -0,0 +1,95 @@
package it.integry.integrywmsnative.ui.fastscroll.viewprovider;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.Nullable;
import it.integry.integrywmsnative.ui.fastscroll.FastScroller;
/**
* Created by Michal on 05/08/16.
* Provides {@link View}s and their behaviors for the handle and bubble of the fastscroller.
*/
public abstract class ScrollerViewProvider {
private FastScroller scroller;
private ViewBehavior handleBehavior;
private ViewBehavior bubbleBehavior;
public void setFastScroller(FastScroller scroller){
this.scroller = scroller;
}
protected Context getContext(){
return scroller.getContext();
}
protected FastScroller getScroller() {
return scroller;
}
/**
* @param container The container {@link FastScroller} for the view to inflate properly.
* @return A view which will be by the {@link FastScroller} used as a handle.
*/
public abstract View provideHandleView(ViewGroup container);
/**
* @param container The container {@link FastScroller} for the view to inflate properly.
* @return A view which will be by the {@link FastScroller} used as a bubble.
*/
public abstract View provideBubbleView(ViewGroup container);
/**
* Bubble view has to provide a {@link TextView} that will show the index title.
* @return A {@link TextView} that will hold the index title.
*/
public abstract TextView provideBubbleTextView();
/**
* To offset the position of the bubble relative to the handle. E.g. in {@link DefaultScrollerViewProvider}
* the sharp corner of the bubble is aligned with the center of the handle.
* @return the position of the bubble in relation to the handle (according to the orientation).
*/
public abstract int getBubbleOffset();
@Nullable
protected abstract ViewBehavior provideHandleBehavior();
@Nullable
protected abstract ViewBehavior provideBubbleBehavior();
protected ViewBehavior getHandleBehavior(){
if(handleBehavior==null) handleBehavior = provideHandleBehavior();
return handleBehavior;
}
protected ViewBehavior getBubbleBehavior(){
if(bubbleBehavior==null) bubbleBehavior = provideBubbleBehavior();
return bubbleBehavior;
}
public void onHandleGrabbed(){
if(getHandleBehavior()!=null) getHandleBehavior().onHandleGrabbed();
if(getBubbleBehavior()!=null) getBubbleBehavior().onHandleGrabbed();
}
public void onHandleReleased(){
if(getHandleBehavior()!=null) getHandleBehavior().onHandleReleased();
if(getBubbleBehavior()!=null) getBubbleBehavior().onHandleReleased();
}
public void onScrollStarted(){
if(getHandleBehavior()!=null) getHandleBehavior().onScrollStarted();
if(getBubbleBehavior()!=null) getBubbleBehavior().onScrollStarted();
}
public void onScrollFinished(){
if(getHandleBehavior()!=null) getHandleBehavior().onScrollFinished();
if(getBubbleBehavior()!=null) getBubbleBehavior().onScrollFinished();
}
}

View File

@@ -0,0 +1,14 @@
package it.integry.integrywmsnative.ui.fastscroll.viewprovider;
/**
* Created by Michal on 11/08/16.
* Extending classes should use this interface to get notified about events that occur to the
* fastscroller elements (handle and bubble) and react accordingly. See {@link DefaultBubbleBehavior}
* for an example.
*/
public interface ViewBehavior {
void onHandleGrabbed();
void onHandleReleased();
void onScrollStarted();
void onScrollFinished();
}

View File

@@ -0,0 +1,130 @@
package it.integry.integrywmsnative.ui.fastscroll.viewprovider;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.view.View;
import androidx.annotation.AnimatorRes;
import it.integry.integrywmsnative.R;
/**
* Created by Michal on 05/08/16.
* Animates showing and hiding elements of the {@link it.integry.integrywmsnative.ui.fastscroll.FastScroller} (handle and bubble).
* The decision when to show/hide the element should be implemented via {@link ViewBehavior}.
*/
public class VisibilityAnimationManager {
protected final View view;
protected AnimatorSet hideAnimator;
protected AnimatorSet showAnimator;
private float pivotXRelative;
private float pivotYRelative;
protected VisibilityAnimationManager(final View view, @AnimatorRes int showAnimator, @AnimatorRes int hideAnimator, float pivotXRelative, float pivotYRelative, int hideDelay){
this.view = view;
this.pivotXRelative = pivotXRelative;
this.pivotYRelative = pivotYRelative;
this.hideAnimator = (AnimatorSet) AnimatorInflater.loadAnimator(view.getContext(), hideAnimator);
this.hideAnimator.setStartDelay(hideDelay);
this.hideAnimator.setTarget(view);
this.showAnimator = (AnimatorSet) AnimatorInflater.loadAnimator(view.getContext(), showAnimator);
this.showAnimator.setTarget(view);
this.hideAnimator.addListener(new AnimatorListenerAdapter() {
//because onAnimationEnd() goes off even for canceled animations
boolean wasCanceled;
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if(!wasCanceled) view.setVisibility(View.INVISIBLE);
wasCanceled = false;
}
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
wasCanceled = true;
}
});
updatePivot();
}
public void show(){
hideAnimator.cancel();
if (view.getVisibility() == View.INVISIBLE) {
view.setVisibility(View.VISIBLE);
updatePivot();
showAnimator.start();
}
}
public void hide(){
updatePivot();
hideAnimator.start();
}
protected void updatePivot() {
view.setPivotX(pivotXRelative*view.getMeasuredWidth());
view.setPivotY(pivotYRelative*view.getMeasuredHeight());
}
public static abstract class AbsBuilder<T extends VisibilityAnimationManager> {
protected final View view;
protected int showAnimatorResource = R.animator.fastscroll__default_show;
protected int hideAnimatorResource = R.animator.fastscroll__default_hide;
protected int hideDelay = 1000;
protected float pivotX = 0.5f;
protected float pivotY = 0.5f;
public AbsBuilder(View view) {
this.view = view;
}
public AbsBuilder<T> withShowAnimator(@AnimatorRes int showAnimatorResource){
this.showAnimatorResource = showAnimatorResource;
return this;
}
public AbsBuilder<T> withHideAnimator(@AnimatorRes int hideAnimatorResource){
this.hideAnimatorResource = hideAnimatorResource;
return this;
}
public AbsBuilder<T> withHideDelay(int hideDelay){
this.hideDelay = hideDelay;
return this;
}
public AbsBuilder<T> withPivotX(float pivotX){
this.pivotX = pivotX;
return this;
}
public AbsBuilder<T> withPivotY(float pivotY){
this.pivotY = pivotY;
return this;
}
public abstract T build();
}
public static class Builder extends AbsBuilder<VisibilityAnimationManager> {
public Builder(View view) {
super(view);
}
public VisibilityAnimationManager build(){
return new VisibilityAnimationManager(view, showAnimatorResource, hideAnimatorResource, pivotX, pivotY, hideDelay);
}
}
}