Compose M3 redesign: custom emoji keyboard
This commit is contained in:
parent
15883f2138
commit
34a2af8429
|
@ -20,13 +20,16 @@ import android.text.TextWatcher;
|
||||||
import android.text.style.BackgroundColorSpan;
|
import android.text.style.BackgroundColorSpan;
|
||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.SoundEffectConstants;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewOutlineProvider;
|
import android.view.ViewOutlineProvider;
|
||||||
|
import android.view.ViewTreeObserver;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.view.inputmethod.InputConnection;
|
import android.view.inputmethod.InputConnection;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
@ -223,7 +226,18 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
||||||
creatingView=true;
|
creatingView=true;
|
||||||
emojiKeyboard=new CustomEmojiPopupKeyboard(getActivity(), customEmojis, instanceDomain);
|
emojiKeyboard=new CustomEmojiPopupKeyboard(getActivity(), customEmojis, instanceDomain);
|
||||||
emojiKeyboard.setListener(this::onCustomEmojiClick);
|
emojiKeyboard.setListener(new CustomEmojiPopupKeyboard.Listener(){
|
||||||
|
@Override
|
||||||
|
public void onEmojiSelected(Emoji emoji){
|
||||||
|
onCustomEmojiClick(emoji);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackspace(){
|
||||||
|
getActivity().dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
|
||||||
|
getActivity().dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
View view=inflater.inflate(R.layout.fragment_compose, container, false);
|
View view=inflater.inflate(R.layout.fragment_compose, container, false);
|
||||||
mainLayout=view.findViewById(R.id.compose_main_ll);
|
mainLayout=view.findViewById(R.id.compose_main_ll);
|
||||||
|
@ -269,6 +283,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
@Override
|
@Override
|
||||||
public void onIconChanged(int icon){
|
public void onIconChanged(int icon){
|
||||||
emojiBtn.setSelected(icon!=PopupKeyboard.ICON_HIDDEN);
|
emojiBtn.setSelected(icon!=PopupKeyboard.ICON_HIDDEN);
|
||||||
|
updateNavigationBarColor(icon!=PopupKeyboard.ICON_HIDDEN);
|
||||||
if(autocompleteViewController.getMode()==ComposeAutocompleteViewController.Mode.EMOJIS){
|
if(autocompleteViewController.getMode()==ComposeAutocompleteViewController.Mode.EMOJIS){
|
||||||
contentView.layout(contentView.getLeft(), contentView.getTop(), contentView.getRight(), contentView.getBottom());
|
contentView.layout(contentView.getLeft(), contentView.getTop(), contentView.getRight(), contentView.getBottom());
|
||||||
if(icon==PopupKeyboard.ICON_HIDDEN)
|
if(icon==PopupKeyboard.ICON_HIDDEN)
|
||||||
|
@ -281,7 +296,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
|
|
||||||
contentView=(SizeListenerLinearLayout) view;
|
contentView=(SizeListenerLinearLayout) view;
|
||||||
contentView.addView(emojiKeyboard.getView());
|
contentView.addView(emojiKeyboard.getView());
|
||||||
emojiKeyboard.getView().setElevation(V.dp(2));
|
|
||||||
|
|
||||||
spoilerEdit=view.findViewById(R.id.content_warning);
|
spoilerEdit=view.findViewById(R.id.content_warning);
|
||||||
spoilerWrap=view.findViewById(R.id.content_warning_wrap);
|
spoilerWrap=view.findViewById(R.id.content_warning_wrap);
|
||||||
|
@ -608,8 +622,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
int color=UiUtils.alphaBlendThemeColors(getActivity(), R.attr.colorM3Background, R.attr.colorM3Primary, 0.11f);
|
int color=UiUtils.alphaBlendThemeColors(getActivity(), R.attr.colorM3Background, R.attr.colorM3Primary, 0.11f);
|
||||||
getToolbar().setBackgroundColor(color);
|
getToolbar().setBackgroundColor(color);
|
||||||
setStatusBarColor(color);
|
setStatusBarColor(color);
|
||||||
setNavigationBarColor(color);
|
|
||||||
bottomBar.setBackgroundColor(color);
|
bottomBar.setBackgroundColor(color);
|
||||||
|
updateNavigationBarColor(emojiKeyboard.isVisible());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateNavigationBarColor(boolean emojiKeyboardVisible){
|
||||||
|
int color=UiUtils.alphaBlendThemeColors(getActivity(), R.attr.colorM3Background, R.attr.colorM3Primary, emojiKeyboardVisible ? 0.08f : 0.11f);
|
||||||
|
setNavigationBarColor(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -2,14 +2,19 @@ package org.joinmastodon.android.ui;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.ColorStateList;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.Animatable;
|
import android.graphics.drawable.Animatable;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.view.Gravity;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
|
@ -22,7 +27,6 @@ import org.joinmastodon.android.model.EmojiCategory;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
@ -45,9 +49,8 @@ public class CustomEmojiPopupKeyboard extends PopupKeyboard{
|
||||||
private ListImageLoaderWrapper imgLoader;
|
private ListImageLoaderWrapper imgLoader;
|
||||||
private MergeRecyclerAdapter adapter=new MergeRecyclerAdapter();
|
private MergeRecyclerAdapter adapter=new MergeRecyclerAdapter();
|
||||||
private String domain;
|
private String domain;
|
||||||
private int gridGap;
|
|
||||||
private int spanCount=6;
|
private int spanCount=6;
|
||||||
private Consumer<Emoji> listener;
|
private Listener listener;
|
||||||
|
|
||||||
public CustomEmojiPopupKeyboard(Activity activity, List<EmojiCategory> emojis, String domain){
|
public CustomEmojiPopupKeyboard(Activity activity, List<EmojiCategory> emojis, String domain){
|
||||||
super(activity);
|
super(activity);
|
||||||
|
@ -62,11 +65,8 @@ public class CustomEmojiPopupKeyboard extends PopupKeyboard{
|
||||||
@Override
|
@Override
|
||||||
protected void onMeasure(int widthSpec, int heightSpec){
|
protected void onMeasure(int widthSpec, int heightSpec){
|
||||||
// it's important to do this in onMeasure so the child views will be measured with correct paddings already set
|
// it's important to do this in onMeasure so the child views will be measured with correct paddings already set
|
||||||
spanCount=Math.round(MeasureSpec.getSize(widthSpec)/(float)V.dp(44+20));
|
spanCount=Math.round((MeasureSpec.getSize(widthSpec)-V.dp(32-8))/(float)V.dp(48+8));
|
||||||
lm.setSpanCount(spanCount);
|
lm.setSpanCount(spanCount);
|
||||||
int pad=V.dp(16);
|
|
||||||
gridGap=(MeasureSpec.getSize(widthSpec)-pad*2-V.dp(44)*spanCount)/(spanCount-1);
|
|
||||||
setPadding(pad, 0, pad-gridGap, 0);
|
|
||||||
invalidateItemDecorations();
|
invalidateItemDecorations();
|
||||||
super.onMeasure(widthSpec, heightSpec);
|
super.onMeasure(widthSpec, heightSpec);
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,7 @@ public class CustomEmojiPopupKeyboard extends PopupKeyboard{
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
list.setLayoutManager(lm);
|
list.setLayoutManager(lm);
|
||||||
|
list.setPadding(V.dp(16), 0, V.dp(16), 0);
|
||||||
imgLoader=new ListImageLoaderWrapper(activity, list, new RecyclerViewDelegate(list), null);
|
imgLoader=new ListImageLoaderWrapper(activity, list, new RecyclerViewDelegate(list), null);
|
||||||
|
|
||||||
for(EmojiCategory category:emojis)
|
for(EmojiCategory category:emojis)
|
||||||
|
@ -88,22 +89,52 @@ public class CustomEmojiPopupKeyboard extends PopupKeyboard{
|
||||||
list.addItemDecoration(new RecyclerView.ItemDecoration(){
|
list.addItemDecoration(new RecyclerView.ItemDecoration(){
|
||||||
@Override
|
@Override
|
||||||
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
|
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
|
||||||
outRect.right=gridGap;
|
|
||||||
if(view instanceof TextView){ // section header
|
if(view instanceof TextView){ // section header
|
||||||
if(parent.getChildAdapterPosition(view)>0)
|
outRect.left=outRect.right=V.dp(-16);
|
||||||
outRect.top=-gridGap; // negate the margin added by the emojis above
|
|
||||||
}else{
|
}else{
|
||||||
outRect.bottom=gridGap;
|
EmojiViewHolder evh=(EmojiViewHolder) parent.getChildViewHolder(view);
|
||||||
|
int col=evh.positionWithinCategory%spanCount;
|
||||||
|
if(col<spanCount-1){
|
||||||
|
outRect.right=V.dp(8);
|
||||||
|
}
|
||||||
|
outRect.bottom=V.dp(8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
list.setBackgroundColor(UiUtils.getThemeColor(activity, android.R.attr.colorBackground));
|
|
||||||
list.setSelector(null);
|
list.setSelector(null);
|
||||||
|
list.setClipToPadding(false);
|
||||||
|
new StickyHeadersOverlay(activity, 0).install(list);
|
||||||
|
|
||||||
return list;
|
LinearLayout ll=new LinearLayout(activity);
|
||||||
|
ll.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
ll.setElevation(V.dp(3));
|
||||||
|
ll.setBackgroundResource(R.drawable.bg_m3_surface1);
|
||||||
|
|
||||||
|
ll.addView(list, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
|
||||||
|
|
||||||
|
FrameLayout bottomPanel=new FrameLayout(activity);
|
||||||
|
bottomPanel.setPadding(V.dp(16), V.dp(8), V.dp(16), V.dp(8));
|
||||||
|
bottomPanel.setBackgroundResource(R.drawable.bg_m3_surface2);
|
||||||
|
ll.addView(bottomPanel, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
ImageButton hideKeyboard=new ImageButton(activity);
|
||||||
|
hideKeyboard.setImageResource(R.drawable.ic_keyboard_hide_24px);
|
||||||
|
hideKeyboard.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(activity, R.attr.colorM3OnSurfaceVariant)));
|
||||||
|
hideKeyboard.setBackgroundResource(R.drawable.bg_round_ripple);
|
||||||
|
hideKeyboard.setOnClickListener(v->hide());
|
||||||
|
bottomPanel.addView(hideKeyboard, new FrameLayout.LayoutParams(V.dp(36), V.dp(36), Gravity.LEFT));
|
||||||
|
|
||||||
|
ImageButton backspace=new ImageButton(activity);
|
||||||
|
backspace.setImageResource(R.drawable.ic_backspace_24px);
|
||||||
|
backspace.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(activity, R.attr.colorM3OnSurfaceVariant)));
|
||||||
|
backspace.setBackgroundResource(R.drawable.bg_round_ripple);
|
||||||
|
backspace.setOnClickListener(v->listener.onBackspace());
|
||||||
|
bottomPanel.addView(backspace, new FrameLayout.LayoutParams(V.dp(36), V.dp(36), Gravity.RIGHT));
|
||||||
|
|
||||||
|
return ll;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setListener(Consumer<Emoji> listener){
|
public void setListener(Listener listener){
|
||||||
this.listener=listener;
|
this.listener=listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +154,7 @@ public class CustomEmojiPopupKeyboard extends PopupKeyboard{
|
||||||
public SingleCategoryAdapter(EmojiCategory category){
|
public SingleCategoryAdapter(EmojiCategory category){
|
||||||
super(imgLoader);
|
super(imgLoader);
|
||||||
this.category=category;
|
this.category=category;
|
||||||
requests=category.emojis.stream().map(e->new UrlImageLoaderRequest(e.url, V.dp(44), V.dp(44))).collect(Collectors.toList());
|
requests=category.emojis.stream().map(e->new UrlImageLoaderRequest(e.url, V.dp(24), V.dp(24))).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -134,11 +165,11 @@ public class CustomEmojiPopupKeyboard extends PopupKeyboard{
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position){
|
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position){
|
||||||
if(holder instanceof EmojiViewHolder){
|
if(holder instanceof EmojiViewHolder evh){
|
||||||
((EmojiViewHolder) holder).bind(category.emojis.get(position-1));
|
evh.bind(category.emojis.get(position-1));
|
||||||
((EmojiViewHolder) holder).positionWithinCategory=position-1;
|
evh.positionWithinCategory=position-1;
|
||||||
}else if(holder instanceof SectionHeaderViewHolder){
|
}else if(holder instanceof SectionHeaderViewHolder shvh){
|
||||||
((SectionHeaderViewHolder) holder).bind(TextUtils.isEmpty(category.title) ? domain : category.title);
|
shvh.bind(TextUtils.isEmpty(category.title) ? domain : category.title);
|
||||||
}
|
}
|
||||||
super.onBindViewHolder(holder, position);
|
super.onBindViewHolder(holder, position);
|
||||||
}
|
}
|
||||||
|
@ -164,14 +195,24 @@ public class CustomEmojiPopupKeyboard extends PopupKeyboard{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SectionHeaderViewHolder extends BindableViewHolder<String>{
|
private class SectionHeaderViewHolder extends BindableViewHolder<String> implements StickyHeadersOverlay.HeaderViewHolder{
|
||||||
|
private Drawable background;
|
||||||
|
|
||||||
public SectionHeaderViewHolder(){
|
public SectionHeaderViewHolder(){
|
||||||
super(activity, R.layout.item_emoji_section, list);
|
super(activity, R.layout.item_emoji_section, list);
|
||||||
|
background=new ColorDrawable(UiUtils.alphaBlendThemeColors(activity, R.attr.colorM3Surface, R.attr.colorM3Primary, .08f));
|
||||||
|
itemView.setBackground(background);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(String item){
|
public void onBind(String item){
|
||||||
((TextView)itemView).setText(item);
|
((TextView)itemView).setText(item);
|
||||||
|
setStickyFactor(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStickyFactor(float factor){
|
||||||
|
background.setAlpha(Math.round(255*factor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,8 +221,11 @@ public class CustomEmojiPopupKeyboard extends PopupKeyboard{
|
||||||
public EmojiViewHolder(){
|
public EmojiViewHolder(){
|
||||||
super(new ImageView(activity));
|
super(new ImageView(activity));
|
||||||
ImageView img=(ImageView) itemView;
|
ImageView img=(ImageView) itemView;
|
||||||
img.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(44)));
|
img.setLayoutParams(new RecyclerView.LayoutParams(V.dp(48), V.dp(48)));
|
||||||
img.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
img.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||||
|
int pad=V.dp(12);
|
||||||
|
img.setPadding(pad, pad, pad, pad);
|
||||||
|
img.setBackgroundResource(R.drawable.bg_custom_emoji);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -203,7 +247,12 @@ public class CustomEmojiPopupKeyboard extends PopupKeyboard{
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(){
|
public void onClick(){
|
||||||
listener.accept(item);
|
listener.onEmojiSelected(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface Listener{
|
||||||
|
void onEmojiSelected(Emoji emoji);
|
||||||
|
void onBackspace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
package org.joinmastodon.android.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
public class StickyHeadersOverlay{
|
||||||
|
private static final String TAG="StickyHeadersOverlay";
|
||||||
|
|
||||||
|
private FrameLayout headerWrapper;
|
||||||
|
private Context context;
|
||||||
|
private RecyclerView parent;
|
||||||
|
private RecyclerView.ViewHolder currentHeaderHolder;
|
||||||
|
private int headerViewType;
|
||||||
|
|
||||||
|
public StickyHeadersOverlay(Context context, int headerViewType){
|
||||||
|
this.context=context;
|
||||||
|
this.headerViewType=headerViewType;
|
||||||
|
headerWrapper=new FrameLayout(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void install(RecyclerView parent){
|
||||||
|
if(this.parent!=null)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
this.parent=parent;
|
||||||
|
parent.getViewTreeObserver().addOnPreDrawListener(()->{
|
||||||
|
if(parent.getWidth()!=headerWrapper.getWidth() || parent.getHeight()!=headerWrapper.getHeight()){
|
||||||
|
headerWrapper.measure(parent.getWidth() | View.MeasureSpec.EXACTLY, parent.getHeight() | View.MeasureSpec.EXACTLY);
|
||||||
|
headerWrapper.layout(0, 0, parent.getWidth(), parent.getHeight());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
parent.getOverlay().add(headerWrapper);
|
||||||
|
|
||||||
|
parent.addOnScrollListener(new RecyclerView.OnScrollListener(){
|
||||||
|
@Override
|
||||||
|
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
|
||||||
|
if(currentHeaderHolder==null){
|
||||||
|
currentHeaderHolder=parent.getAdapter().createViewHolder(parent, headerViewType);
|
||||||
|
headerWrapper.addView(currentHeaderHolder.itemView);
|
||||||
|
}
|
||||||
|
int firstVisiblePos=parent.getChildAdapterPosition(parent.getChildAt(0));
|
||||||
|
RecyclerView.Adapter<RecyclerView.ViewHolder> adapter=Objects.requireNonNull(parent.getAdapter());
|
||||||
|
// Go backwards from the first visible position to find the previous header
|
||||||
|
for(int i=firstVisiblePos;i>=0;i--){
|
||||||
|
if(adapter.getItemViewType(i)==headerViewType){
|
||||||
|
if(currentHeaderHolder.getAbsoluteAdapterPosition()!=i){
|
||||||
|
adapter.bindViewHolder(currentHeaderHolder, i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(currentHeaderHolder instanceof HeaderViewHolder hvh){
|
||||||
|
hvh.setStickyFactor(firstVisiblePos==0 && parent.getChildAt(0).getTop()==0 ? 0 : 1);
|
||||||
|
}
|
||||||
|
// Now go forward and find the next header view to possibly offset the current one
|
||||||
|
for(int i=firstVisiblePos+1;i<adapter.getItemCount();i++){
|
||||||
|
if(adapter.getItemViewType(i)==headerViewType){
|
||||||
|
RecyclerView.ViewHolder holder=parent.findViewHolderForAdapterPosition(i);
|
||||||
|
if(holder!=null){
|
||||||
|
float factor;
|
||||||
|
if(holder.itemView.getTop()<currentHeaderHolder.itemView.getBottom()){
|
||||||
|
currentHeaderHolder.itemView.setTranslationY(holder.itemView.getTop()-currentHeaderHolder.itemView.getBottom());
|
||||||
|
factor=1f-holder.itemView.getTop()/(float)currentHeaderHolder.itemView.getBottom();
|
||||||
|
}else{
|
||||||
|
currentHeaderHolder.itemView.setTranslationY(0);
|
||||||
|
factor=0;
|
||||||
|
}
|
||||||
|
if(holder instanceof HeaderViewHolder hvh)
|
||||||
|
hvh.setStickyFactor(factor);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface HeaderViewHolder{
|
||||||
|
void setStickyFactor(float factor);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="?colorM3Primary" android:alpha="0.05"/>
|
||||||
|
</selector>
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="?colorM3Primary" android:alpha="0.08"/>
|
||||||
|
</selector>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/m3_on_surface_variant_overlay">
|
||||||
|
<item android:id="@android:id/mask" android:gravity="center" android:width="40dp" android:height="40dp">
|
||||||
|
<shape android:shape="oval">
|
||||||
|
<solid android:color="#000"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:tint="@color/m3_primary_alpha5"
|
||||||
|
android:tintMode="src_over">
|
||||||
|
<solid android:color="?colorM3Surface" />
|
||||||
|
</shape>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:tint="@color/m3_primary_alpha8"
|
||||||
|
android:tintMode="src_over">
|
||||||
|
<solid android:color="?colorM3Surface" />
|
||||||
|
</shape>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M11.4,16 L14,13.4 16.6,16 18,14.6 15.4,12 18,9.4 16.6,8 14,10.6 11.4,8 10,9.4 12.6,12 10,14.6ZM3,12 L7.35,5.85Q7.625,5.45 8.062,5.225Q8.5,5 9,5H19Q19.825,5 20.413,5.588Q21,6.175 21,7V17Q21,17.825 20.413,18.413Q19.825,19 19,19H9Q8.5,19 8.062,18.775Q7.625,18.55 7.35,18.15ZM5.45,12 L9,17Q9,17 9,17Q9,17 9,17H19Q19,17 19,17Q19,17 19,17V7Q19,7 19,7Q19,7 19,7H9Q9,7 9,7Q9,7 9,7ZM19,12V7Q19,7 19,7Q19,7 19,7Q19,7 19,7Q19,7 19,7V17Q19,17 19,17Q19,17 19,17Q19,17 19,17Q19,17 19,17Z"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M20,17H4Q3.175,17 2.588,16.413Q2,15.825 2,15V5Q2,4.175 2.588,3.587Q3.175,3 4,3H20Q20.825,3 21.413,3.587Q22,4.175 22,5V15Q22,15.825 21.413,16.413Q20.825,17 20,17ZM20,15Q20,15 20,15Q20,15 20,15V5Q20,5 20,5Q20,5 20,5H4Q4,5 4,5Q4,5 4,5V15Q4,15 4,15Q4,15 4,15ZM11,8H13V6H11ZM11,11H13V9H11ZM8,8H10V6H8ZM8,11H10V9H8ZM5,11H7V9H5ZM5,8H7V6H5ZM8,14H16V12H8ZM14,11H16V9H14ZM14,8H16V6H14ZM17,11H19V9H17ZM17,8H19V6H17ZM12,23 L8,19H16ZM4,5Q4,5 4,5Q4,5 4,5V15Q4,15 4,15Q4,15 4,15Q4,15 4,15Q4,15 4,15V5Q4,5 4,5Q4,5 4,5Z"/>
|
||||||
|
</vector>
|
|
@ -2,13 +2,11 @@
|
||||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="36dp"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:fontFamily="sans-serif-medium"
|
android:textAppearance="@style/m3_label_large"
|
||||||
android:textSize="12dp"
|
android:textColor="?colorM3OnSurfaceVariant"
|
||||||
android:textColor="?android:textColorSecondary"
|
android:paddingHorizontal="16dp"
|
||||||
android:textAllCaps="true"
|
android:gravity="center_vertical"
|
||||||
android:paddingTop="24dp"
|
|
||||||
android:paddingBottom="12dp"
|
|
||||||
tools:text="Blob whatever things"/>
|
tools:text="Blob whatever things"/>
|
Loading…
Reference in New Issue