How to swipe zoomed image using ImageViews and PageAdapter - java

I am trying to create an image viewer which can load images from given URLs.The code below implement the User Interface.
My intention is to let user Zoom the image and move to next image with a Swipe event.But the problem is that when i zoom and then swipe , instead of showing the remaining portion , it moves to the next image.
I tried using requestDisallowInterceptTouchEvent in TouchImageView's(https://github.com/MikeOrtiz/TouchImageView) onTouchListener .After this the remaining portion could show but now i cannot go to the next page. I was wondering how this can be achieved as the event can only go to either TouchView or PageAdapter
public class PageActivity extends Activity {
private int numPages = 33;
private TouchImageView[] imageViews = new TouchImageView[numPages];
private String URL = "http://www.smbc-comics.com/comics/200905";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewPager viewPager = new ViewPager(this);
for (int i = 0; i < numPages; i++) {
imageViews[i] = new TouchImageView(this);
imageViews[i].setBackgroundResource(R.drawable.banke);
imageViews[i].setMaxZoom(4f);
}
setContentView(viewPager);
ImagePagerAdapter adapter = new ImagePagerAdapter();
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(2);
}
#SuppressWarnings("unused")
private class ImagePagerAdapter extends PagerAdapter {
#Override
public int getCount() {
return numPages;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((TouchImageView) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Context context = PageActivity.this;
String pageURL = URL;
if (imageViews[position].getDrawable() == null) {
ImageFetcher imagefetcher = new ImageFetcher();
imagefetcher.execute(
pageURL + String.format("%02d", position+1) + ".gif",
String.valueOf(position));
}
((ViewPager) container).addView(imageViews[position], 0);
return imageViews[position];
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((TouchImageView) object);
imageViews[position].setImageDrawable(null);
}
}
public class ImageFetcher extends AsyncTask<String, Integer, Drawable> {
int fillthisPos;
public Drawable doInBackground(String... urls) {
try {
InputStream is = (InputStream) new URL(urls[0]).getContent();
fillthisPos = Integer.parseInt(urls[1]);
Drawable d = Drawable.createFromStream(is, "src name");
return d;
} catch (Exception e) {
return null;
}
}
#Override
protected void onPostExecute(Drawable result) {
super.onPostExecute(result);
imageViews[fillthisPos].setImageDrawable(result);
result = null;
}
}
}

You can add following code in TouchImageView class:
public boolean isZoomed () {
return (normalizedScale > minScale);
}
private void onSwipeEvent(MotionEvent event) {
boolean zoomed = this.isZoomed();
if (!zoomed && (swipeLen > 0)) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
swipeStartPos = (int) event.getRawX();
}
else if (event.getAction() == MotionEvent.ACTION_MOVE) {
int distance = ((int) event.getRawX()) - swipeStartPos;
int swipeVector = SWIPE_RIGHT;
if (distance < 0) swipeVector = SWIPE_LEFT;
if (Math.abs(distance) > swipeLen) {
onSwipeHandler.onSwipe(swipeVector);
swipeStartPos = (int) event.getRawX();
this.setScaleX(1f);
}
else {
int swipeStDist = swipeLen - Math.round(((swipeLen / 100) * 50));
if (Math.abs(distance) > swipeStDist) {
this.setScaleX(0.98f);
}
}
}
else if (event.getAction() == MotionEvent.ACTION_UP) {
swipeStartPos = (int) event.getRawX();
this.setScaleX(1f);
}
}
}
public static int SWIPE_LEFT = 0;
public static int SWIPE_RIGHT = 1;
public int swipeStartPos = 0;
public onSwipeListener onSwipeHandler = new onSwipeListener() {
public void onSwipe(int vector) {}
};
public static int swipeLen = 0;
public void setOnSwipeListener(onSwipeListener c, int swipeLength) {
onSwipeHandler = c;
swipeLen = swipeLength;
}
public interface onSwipeListener {
public void onSwipe(int vector);
}
And also in TouchImageView below line 636 add onSwipeEvent(event) like this:
......
setImageMatrix(matrix);
onSwipeEvent(event);
//
// indicate event was handled
//
return true;
...........
After this from you code you can add swipe event listener, like this:
imageView.setOnSwipeListener(new TouchImageView.onSwipeListener() {
#Override
public void onSwipe(int vector) {
if (vector == TouchImageView.SWIPE_LEFT) {
Log.d("swipe", "swipe left!");
}
else if (vector == TouchImageView.SWIPE_RIGHT) {
Log.d("swipe", "swipe right!");
}
}
}, 200); //length of swiping - 200 dip
This onSwipeListener ignore onswipe where image is zoomed+. It work for me.

Related

implementing Admob Native Advanced with RecyclerView but could not get the exact View Types

I already knew how the RecyclerView with different types of view works but this time I'm trying to add the Native Advance Admob ads to my RecyclerView. I followed theseYoutube Tutorials but there was an error printed to my logcat after the app crushed.
Logcat
java.lang.ClassCastException: com.google.android.gms.internal.ads.zzaeh cannot be cast to mgb.com.sdalyricsplus.Database.Entities.SongsEntity
at mgb.com.sdalyricsplus.newAdapters.DisplayItemAdapter.onBindViewHolder(DisplayItemAdapter.java:98)
at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7065)
at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7107)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6012)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6279)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
I reviewed the tutorial many times hoping that I've missed something that causes the error but it seems that I followed the tutorial correctly.
Here are my Codes
DisplayItemActivity
public class DisplayItemActivity extends AppCompatActivity{
public static final int NUMBER_OF_AD = 5;
AdLoader adLoader;
FastScrollRecyclerView recyclerView;
Global global;
RoomViewModel model;
List<Object> recyclerViewItems = new ArrayList<>();
List<UnifiedNativeAd> nativeAds = new ArrayList<>();
DisplayItemAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_songs);
MobileAds.initialize(this,"ca-app-pub-2493911630710964~1147957926");
recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
global = (Global)getApplication();
model = ViewModelProviders.of(this).get(RoomViewModel.class);
adapter = new DisplayItemAdapter(getLayoutInflater());
recyclerView.setAdapter(adapter);
recyclerViewItems.addAll(model.selectAll());
loadNativeAds();
}
private void loadNativeAds() {
AdLoader.Builder builder = new AdLoader.Builder(this, getResources().getString(R.string.native_advance));
adLoader = builder.forUnifiedNativeAd(new UnifiedNativeAd.OnUnifiedNativeAdLoadedListener() {
#Override
public void onUnifiedNativeAdLoaded(UnifiedNativeAd unifiedNativeAd) {
nativeAds.add(unifiedNativeAd);
if (!adLoader.isLoading()) {
insertAdToList();
}
}
}).withAdListener(new AdListener() {
#Override
public void onAdFailedToLoad(int i) {
super.onAdFailedToLoad(i);
if (!adLoader.isLoading()) {
insertAdToList();
}
}
}).build();
adLoader.loadAds(new AdRequest.Builder().build(), NUMBER_OF_AD);
}
private void insertAdToList() {
int offset = recyclerViewItems.size() / (nativeAds.size() + 1);
int index = 0;
for (UnifiedNativeAd ad : nativeAds) {
recyclerViewItems.add(index,ad);
index = index + offset;
}
adapter.setList(recyclerViewItems);
}
}
And my Adapter
DisplayItemAdapter
public class DisplayItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final int MENU_ITEM_VIEW_TYPE = 0;
private final int UNIFIED_NATIVE_AD_VIEW_TYPE = 1;
private List<Object> recyclerViewItems = new ArrayList<>();
private Global global;
private String searchTxt = "";
private final String newline = System.getProperty("line.separator");
private ClickSongItemListener clickSongItemListener;
private ChangeFavoriteListener changeFavoriteListener;
private LayoutInflater layoutInflater;
public DisplayItemAdapter(LayoutInflater layoutInflater) {
this.layoutInflater = layoutInflater;
}
public void setList(List<Object> list) {
this.recyclerViewItems = list;
notifyDataSetChanged();
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
global = (Global) ((Activity)parent.getContext()).getApplication();
switch (viewType) {
case UNIFIED_NATIVE_AD_VIEW_TYPE:
View adView = LayoutInflater.from(parent.getContext()).inflate(R.layout.native_ad_view,parent,false);
return new UnifiedNativeAdViewHolder(adView);
case MENU_ITEM_VIEW_TYPE :
default:
View songitem = LayoutInflater.from(parent.getContext()).inflate(R.layout.gospel_song_item,parent,false);
return new SongItemViewHolder(songitem);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
switch (viewType) {
case UNIFIED_NATIVE_AD_VIEW_TYPE :
UnifiedNativeAdViewHolder nativeAdViewHolder = (UnifiedNativeAdViewHolder)holder;
UnifiedNativeAd unifiedNativeAd = (UnifiedNativeAd) recyclerViewItems.get(position);
// poulateNativeAdView(unifiedNativeAd,((UnifiedNativeAdViewHolder)holder).getAdView());
FrameLayout frameLayout =
nativeAdViewHolder.view.findViewById(R.id.ad_frame_placement);
UnifiedNativeAdView adView = (UnifiedNativeAdView) layoutInflater
.inflate(R.layout.ad_unified, null);
populateUnifiedNativeAdView(unifiedNativeAd, adView);
frameLayout.removeAllViews();
frameLayout.addView(adView);
break;
case MENU_ITEM_VIEW_TYPE :
default:
SongItemViewHolder songItemViewHolder = (SongItemViewHolder)holder;
setSongViews(songItemViewHolder, (SongsEntity)recyclerViewItems.get(position));
}
}
private void populateUnifiedNativeAdView(UnifiedNativeAd nativeAd, UnifiedNativeAdView adView) {
// Set the media view.
adView.setMediaView((MediaView) adView.findViewById(R.id.ad_media));
// Set other ad assets.
adView.setHeadlineView(adView.findViewById(R.id.ad_headline));
adView.setBodyView(adView.findViewById(R.id.ad_body));
adView.setCallToActionView(adView.findViewById(R.id.ad_call_to_action));
adView.setIconView(adView.findViewById(R.id.ad_app_icon));
adView.setPriceView(adView.findViewById(R.id.ad_price));
adView.setStarRatingView(adView.findViewById(R.id.ad_stars));
adView.setStoreView(adView.findViewById(R.id.ad_store));
adView.setAdvertiserView(adView.findViewById(R.id.ad_advertiser));
// The headline and mediaContent are guaranteed to be in every UnifiedNativeAd.
((TextView) adView.getHeadlineView()).setText(nativeAd.getHeadline());
adView.getMediaView().setMediaContent(nativeAd.getMediaContent());
// These assets aren't guaranteed to be in every UnifiedNativeAd, so it's important to
// check before trying to display them.
if (nativeAd.getBody() == null) {
adView.getBodyView().setVisibility(View.INVISIBLE);
} else {
adView.getBodyView().setVisibility(View.VISIBLE);
((TextView) adView.getBodyView()).setText(nativeAd.getBody());
}
if (nativeAd.getCallToAction() == null) {
adView.getCallToActionView().setVisibility(View.INVISIBLE);
} else {
adView.getCallToActionView().setVisibility(View.VISIBLE);
((Button) adView.getCallToActionView()).setText(nativeAd.getCallToAction());
}
if (nativeAd.getIcon() == null) {
adView.getIconView().setVisibility(View.GONE);
} else {
((ImageView) adView.getIconView()).setImageDrawable(
nativeAd.getIcon().getDrawable());
adView.getIconView().setVisibility(View.VISIBLE);
}
if (nativeAd.getPrice() == null) {
adView.getPriceView().setVisibility(View.INVISIBLE);
} else {
adView.getPriceView().setVisibility(View.VISIBLE);
((TextView) adView.getPriceView()).setText(nativeAd.getPrice());
}
if (nativeAd.getStore() == null) {
adView.getStoreView().setVisibility(View.INVISIBLE);
} else {
adView.getStoreView().setVisibility(View.VISIBLE);
((TextView) adView.getStoreView()).setText(nativeAd.getStore());
}
if (nativeAd.getStarRating() == null) {
adView.getStarRatingView().setVisibility(View.INVISIBLE);
} else {
((RatingBar) adView.getStarRatingView())
.setRating(nativeAd.getStarRating().floatValue());
adView.getStarRatingView().setVisibility(View.VISIBLE);
}
if (nativeAd.getAdvertiser() == null) {
adView.getAdvertiserView().setVisibility(View.INVISIBLE);
} else {
((TextView) adView.getAdvertiserView()).setText(nativeAd.getAdvertiser());
adView.getAdvertiserView().setVisibility(View.VISIBLE);
}
// This method tells the Google Mobile Ads SDK that you have finished populating your
// native ad view with this native ad.
adView.setNativeAd(nativeAd);
// Get the video controller for the ad. One will always be provided, even if the ad doesn't
// have a video asset.
VideoController vc = nativeAd.getVideoController();
// Updates the UI to say whether or not this ad has a video asset.
if (vc.hasVideoContent()) {
// videoStatus.setText(String.format(Locale.getDefault(),
// "Video status: Ad contains a %.2f:1 video asset.",
// vc.getAspectRatio()));
// Create a new VideoLifecycleCallbacks object and pass it to the VideoController. The
// VideoController will call methods on this object when events occur in the video
// lifecycle.
vc.setVideoLifecycleCallbacks(new VideoController.VideoLifecycleCallbacks() {
#Override
public void onVideoEnd() {
// Publishers should allow native ads to complete video playback before
// refreshing or replacing them with another ad in the same UI location.
super.onVideoEnd();
}
});
} else {
}
}
private void setSongViews(SongItemViewHolder viewHolder, SongsEntity note) {
Context context = viewHolder.itemView.getContext();
if (note.getMedia_extension().equals("audio")) {
Glide.with(context)
.load(R.drawable.music_icon)
.thumbnail(00.1f)
.into(viewHolder.iv_thumbnail);
}else if (note.getGenre().toLowerCase().contains("karaoke")) {
File file = new File(context.getExternalFilesDir(null)+"/.file"+note.getId());
if (file.exists()) {
Glide.with(context)
.setDefaultRequestOptions(new RequestOptions().placeholder(R.drawable.karaoke_icon))
.load(file)
.thumbnail(00.1f)
.into(viewHolder.iv_thumbnail);
}else {
Glide.with(context)
.setDefaultRequestOptions(new RequestOptions().placeholder(R.drawable.karaoke_icon))
.load(note.getMedia_url())
.thumbnail(00.1f)
.into(viewHolder.iv_thumbnail);
}
}else {
Glide.with(context)
.load(R.drawable.lyrics_icon)
.thumbnail(00.1f)
.into(viewHolder.iv_thumbnail);
}
viewHolder.title.setText(global.capitalize(note.getTitle()));
viewHolder.artist.setText(global.capitalize(note.getArtist()));
viewHolder.category.setText(note.getGenre());
viewHolder.favorite.setOnCheckedChangeListener(null);
viewHolder.favorite.setChecked(note.getFavorites());
viewHolder.views.setText(note.getFavorite_counter() <2 ? note.getFavorite_counter()+" heart" : note.getFavorite_counter()+" hearts");
String MY_ID = "JUntYdabhUh5XtMhfCIXXwNbsdW2";
if (!note.getUploader_id().equals(MY_ID))
Glide.with(context)
.setDefaultRequestOptions(new RequestOptions().placeholder(R.mipmap.sda_logo).diskCacheStrategy(DiskCacheStrategy.ALL))
.load(note.getUploader_photo_url())
.into(viewHolder.user_logo);
else
Glide.with(context)
.load(R.mipmap.sda_logo)
.into(viewHolder.user_logo);
String lyrics = note.getLyrics().toLowerCase();
String searchFilter = searchTxt.toLowerCase();
if (searchTxt.isEmpty()) {
viewHolder.phrase_end.setText(note.getLyrics());
viewHolder.phrase.setText("");
} else
if (lyrics.contains(searchFilter) && (lyrics.indexOf(searchFilter)) + searchTxt.length() <= lyrics.length()) {
viewHolder.phrase.setText(searchTxt);
String filter = note.getLyrics().substring(lyrics.indexOf(searchFilter) + searchFilter.length());
assert newline != null;
viewHolder.phrase_end.setText(filter.replaceAll(newline, " "));
}else {
viewHolder.phrase_end.setText(note.getLyrics());
viewHolder.phrase.setText("");
}
viewHolder.favorite.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (changeFavoriteListener != null) {
changeFavoriteListener.onChange(buttonView.getContext(),note,isChecked);
}
}
});
}
private void poulateNativeAdView(UnifiedNativeAd unifiedNativeAd, UnifiedNativeAdView adView) {
((TextView)adView.getHeadlineView()).setText(unifiedNativeAd.getHeadline());
((TextView)adView.getBodyView()).setText(unifiedNativeAd.getBody());
((TextView)adView.getCallToActionView()).setText(unifiedNativeAd.getCallToAction());
NativeAd.Image icon = unifiedNativeAd.getIcon();
if (icon == null) {
adView.getIconView().setVisibility(View.INVISIBLE);
}else {
((ImageView)adView.getIconView()).setImageDrawable(icon.getDrawable());
adView.getIconView().setVisibility(View.VISIBLE);
}
if (unifiedNativeAd.getPrice() == null) {
adView.getPriceView().setVisibility(View.INVISIBLE);
}else {
adView.getPriceView().setVisibility(View.VISIBLE);
((TextView)adView.getPriceView()).setText(unifiedNativeAd.getPrice());
}
if (unifiedNativeAd.getStore() == null) {
adView.getStoreView().setVisibility(View.INVISIBLE);
}else {
adView.getStoreView().setVisibility(View.VISIBLE);
((TextView)adView.getStoreView()).setText(unifiedNativeAd.getStore());
}
if (unifiedNativeAd.getStarRating() == null) {
adView.getStarRatingView().setVisibility(View.INVISIBLE);
}else {
adView.getStarRatingView().setVisibility(View.VISIBLE);
((RatingBar)adView.getStarRatingView()).setRating(unifiedNativeAd.getStarRating().floatValue());
}
if (unifiedNativeAd.getAdvertiser() == null) {
adView.getAdvertiserView().setVisibility(View.INVISIBLE);
}else {
adView.getAdvertiserView().setVisibility(View.VISIBLE);
((TextView)adView.getAdvertiserView()).setText(unifiedNativeAd.getAdvertiser());
}
adView.setNativeAd(unifiedNativeAd);
}
#Override
public int getItemCount() {
return recyclerViewItems.size();
}
public class SongItemViewHolder extends RecyclerView.ViewHolder {
TextView title, phrase,phrase_end;
TextView artist;
TextView category;
ToggleButton favorite;
LinearLayout layoutWrapper;
LinearLayout phrase_layout;
TextView views;
CircleImageView user_logo;
ImageView iv_thumbnail;
public SongItemViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.text_view_title);
iv_thumbnail = itemView.findViewById(R.id.iv_thumbnail);
artist = itemView.findViewById(R.id.text_view_artist);
phrase = itemView.findViewById(R.id.phrase);
phrase_end = itemView.findViewById(R.id.phrase_end);
category = itemView.findViewById(R.id.text_view_category);
favorite = itemView.findViewById(R.id.toggleButton_favorite);
layoutWrapper = itemView.findViewById(R.id.layout_wrapper);
phrase_layout = itemView.findViewById(R.id.phrase_layout);
views = itemView.findViewById(R.id.tv_view_status);
user_logo = itemView.findViewById(R.id.user_logo);
}
}
private class UnifiedNativeAdViewHolder extends RecyclerView.ViewHolder {
private View view;
public UnifiedNativeAdViewHolder(View view) {
super(view);
this.view = view;
}
}
}
the logcat says that com.google.android.gms.internal.ads.zzaeh cannot be cast to mgb.com.sdalyricsplus.Database.Entities.SongsEntity and the error was pointed to the viewbindholder
case MENU_ITEM_VIEW_TYPE :
default:
SongItemViewHolder songItemViewHolder = (SongItemViewHolder)holder;
setSongViews(songItemViewHolder, (SongsEntity)recyclerViewItems.get(position)); // this line
my suspect is the viewtype. Maybe the view type was not correctly assigned.
I tried this code also but the error still there.
#Override
public int getItemViewType(int position) {
if (position % DisplayItemActivity.NUMBER_OF_AD == 0) {
return UNIFIED_NATIVE_AD_VIEW_TYPE;
}else {
return MENU_ITEM_VIEW_TYPE;
}
}
can someone help me to find the cause of the error?
You need to merge your SongItem and UnifiedNativeAd to become 1 single data source.
Make a new object class name SongAdsData to merge your Song object and Ads from Admob into 1 single object like below:
public class SongAdsData {
public int getType() {
return type;
}
public UnifiedNativeAd getAds() {
return ads;
}
public Post getSong() {
return Song;
}
public int type; // 1 is ads and 2 is songs
public UnifiedNativeAd ads;
public Song song; // here is ur Song object as usual
}
In your adapter, modify it as below:
public class SongAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int UNIFIED_ADS_VIEW = 1;
private static final int SONG_ITEM_VIEW = 2;
private List<SongAdsData> songAdsDataList;
//here your onBindViewHolder,here your refer to item of `songAdsDataList`
// here your onCreateViewHolder
public void setSongAdsDataList(List<SongAdsData> songAdsDataForRecyclerView) {
this.songAdsDataList = songAdsDataForRecyclerView;
notifyDataSetChanged();
}
// Here is the function that insert the ads to RecyclerView
public void insertAdToRecyclerView(List<UnifiedNativeAds> nativeAds) {
int offset = songAdsDataList.size() / (nativeAds.size() + 1);
int index = 0;
for (UnifiedNativeAd ad : nativeAds) {
SongAdsData adsData = new SongAdsData();
adsData.song = null;
adsData.ads = ad;
adsData.type = 1; //1 for ads,2 for song
songAdsDataList.add(index,adsData);
index = index + offset;
}
notifyDataSetChanged();
}
#Override
public int getItemViewType (int position) {
if(songAdsDataList.get(position).getType() == 1){ // So here you compare the type of the object it the position
return UNIFIED_ADS;
}else{
return SONG_ITEM_VIEW;
}
}
#Override
public int getItemCount() {
if(songAdsDataList != null){
return songAdsDataList.size();
}else{
return 0;
}
}
}
So finally in your DisplayItemActivity
I not sure how you get your SongItem(your data from server or somewhere else),but the idea is transform your SongItem into SongAdsData that we created in the step one,
Example like this:
private void displaySongFromYourServer(List<Songs> songs) {
List<SongAdsData> songAdsDataList = new ArrayList<>();
for(Songs song : songs){
SongAdsData data = new SongAdsData();
data.ads = null;
data.song = song;
data.type = 2;
songAdsDataList.add(data);
}
songAdapter.setSongAdsDataList(songAdsDataList);
}
So by now,your Song item will become SongAdsData. At the same time, your UnifiedNativeAd object also need to transform become SongAdsData, therefore we need to
move all thing inside insertAdToList into SongAdapter(See the insertAdToRecyclerView in Song adapter above),so the it can refer to the same List of the recyclerView.
Therefore in your insertAdToList of DisplayItemActivity should become like this:
private void insertAdToList() {
songAdapter.insertAdsToRecyclerView(nativeAds); //called to the function inside songAdapter.
}
Hope you get the idea.
You need to override getItemViewType(int position) to check the instance of your Object in order to return the right view type:
#Override
public int getItemViewType(int position) {
if (this.recyclerViewItems.get(position) instanceof UnifiedNativeAd) {
return UNIFIED_NATIVE_AD_VIEW_TYPE;
}else {
return MENU_ITEM_VIEW_TYPE;
}
}

Android - NullPointerException SimpleAdapter - Pinned section ListView?

I created a ListView with pinned which works well once. But when I arrive latest item of ListView crashes on me and says NullPointerException.
I need that when I arrive latest item get next 5 of items .
MainActivity.java :
public class PinnedSectionListActivity extends ListActivity implements OnClickListener {
public static Context context;
public static Context _context;
public static SQLiteDatabase sql;
public Cursor cursor;
public Cursor cursorA;
public boolean flag = false;
public Integer count = 0;
public List<Items> result;
public List<ItemsT> resultT;
static class SimpleAdapter extends ArrayAdapter<Item> implements PinnedSectionListView.PinnedSectionListAdapter {
private List<Items> Results;
private List<ItemsT> ResultsT;
private Integer counts;
private static final int[] COLORS = new int[] {
R.color.green_light, R.color.orange_light,
R.color.blue_light, R.color.red_light };
public SimpleAdapter(Context context,List<Items> MResults,List<ItemsT> MresultT,Integer count , int resource, int textViewResourceId) {
super(context, resource, textViewResourceId);
this.Results = MResults;
this.ResultsT = MresultT;
this.counts = count;
generateDataset(false);
}
public void generateDataset(boolean clear) {
int y = 0;
if (clear) clear();
prepareSections(counts);
int sectionPosition = 0, listPosition = 0;
for (char i=0; i<counts; i++) {
final Items ci = Results.get(i);
Item section = new Item(Item.SECTION, ci.WTitleO);
section.sectionPosition = sectionPosition;
section.listPosition = listPosition++;
onSectionAdded(section, sectionPosition);
add(section);
for (int j=0;j<ci.count;j++) {
final ItemsT xx = ResultsT.get(y);
Item item = new Item(Item.ITEM, xx.WTitleT);
item.sectionPosition = sectionPosition;
item.listPosition = listPosition++;
y++;
add(item);
}
sectionPosition++;
}
}
protected void prepareSections(int sectionsNumber) { }
protected void onSectionAdded(Item section, int sectionPosition) { }
#Override public View getView(int position, View convertView, ViewGroup parent) {
TextView view = (TextView) super.getView(position, convertView, parent);
view.setTextColor(Color.DKGRAY);
view.setTag("" + position);
Item item = getItem(position);
if (item.type == Item.SECTION) {
view.setBackgroundColor(parent.getResources().getColor(COLORS[item.sectionPosition % COLORS.length]));
}
return view;
}
#Override public int getViewTypeCount() {
return 2;
}
#Override public int getItemViewType(int position) {
return getItem(position).type;
}
#Override
public boolean isItemViewTypePinned(int viewType) {
return viewType == Item.SECTION;
}
}
static class Items {
public int conO ;
public String WTitleO;
public int count;
}
static class ItemsT {
public String WTitleT;
}
static class Item {
public static final int ITEM = 0;
public static final int SECTION = 1;
public final int type;
public final String text;
public int sectionPosition;
public int listPosition;
public Item(int type, String text) {
this.type = type;
this.text = text;
}
#Override public String toString() {
return text;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getApplicationContext();
_context = this;
DB db = new DB(context);
db.CreateFile();
try {
db.CreateandOpenDataBase();
} catch (IOException e) {
e.printStackTrace();
}
sql = db.openDataBase();
test();
}
public void test(){
result = new ArrayList<Items>();
resultT = new ArrayList<ItemsT>();
try {
cursor = sql.rawQuery("SELECT (select count() as number from WebSite_BookPageDB22 as b where b.ParentID = a.ContentID) as count" +
" ,ContentID,Title" +
" FROM WebSite_BookPageDB22 as a" +
" WHERE ArticleID = '" + 61799 + "' AND ParentID = '" + 0 + "'order by PageID ASC LIMIT 5", null);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
Items ci = new Items();
ci.conO = cursor.getInt(cursor.getColumnIndex("ContentID"));
ci.WTitleO = cursor.getString(cursor.getColumnIndex("Title"));
ci.count = cursor.getInt(cursor.getColumnIndex("count"));
if(flag == false) {
count = cursor.getCount();
flag = true;
}
cursorA = sql.rawQuery("select Title from WebSite_BookPageDB22 where ParentID = '" + ci.conO +"' order by PageID ASC", null);
try {
if (cursorA != null && cursorA.moveToFirst()) {
do {
ItemsT cty = new ItemsT();
cty.WTitleT = cursorA.getString(cursorA.getColumnIndex("Title"));
resultT.add(cty);
} while (cursorA.moveToNext());
}
}catch (Exception e){
Log.i("xxx", "You have an error");
}finally {
if (cursorA != null) {
cursorA.close();
}
}
result.add(ci);
} while (cursor.moveToNext());
}
}
} catch (Exception e) {
} finally {
cursor.close();
}
SimpleAdapter adapt = new SimpleAdapter(_context, result, resultT, count, android.R.layout.simple_list_item_1, android.R.id.text1);
setListAdapter(adapt);
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Item item = (Item) getListView().getAdapter().getItem(position);
if (item != null) {
Toast.makeText(this, "Item " + position + ": " + item.text, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Item " + position, Toast.LENGTH_SHORT).show();
}
}
#Override
public void onClick(View v) {
Toast.makeText(this, "Item: " + v.getTag() , Toast.LENGTH_SHORT).show();
}
}
And PinnedSectionListView.java :
public class PinnedSectionListView extends ListView {
//-- inner classes
/** List adapter to be implemented for being used with PinnedSectionListView adapter. */
public static interface PinnedSectionListAdapter extends ListAdapter {
/** This method shall return 'true' if views of given type has to be pinned. */
boolean isItemViewTypePinned(int viewType);
}
/** Wrapper class for pinned section view and its position in the list. */
static class PinnedSection {
public View view;
public int position;
public long id;
}
//-- class fields
// fields used for handling touch events
private final Rect mTouchRect = new Rect();
private final PointF mTouchPoint = new PointF();
private int mTouchSlop;
private View mTouchTarget;
private MotionEvent mDownEvent;
// fields used for drawing shadow under a pinned section
private GradientDrawable mShadowDrawable;
private int mSectionsDistanceY;
private int mShadowHeight;
/** Delegating listener, can be null. */
OnScrollListener mDelegateOnScrollListener;
/** Shadow for being recycled, can be null. */
PinnedSection mRecycleSection;
/** shadow instance with a pinned view, can be null. */
PinnedSection mPinnedSection;
/** Pinned view Y-translation. We use it to stick pinned view to the next section. */
int mTranslateY;
/** Scroll listener which does the magic */
private final OnScrollListener mOnScrollListener = new OnScrollListener() {
#Override public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScrollStateChanged(view, scrollState);
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if(firstVisibleItem+visibleItemCount == totalItemCount && totalItemCount!=0)
{
PinnedSectionListActivity xx = new PinnedSectionListActivity();
xx.flag = false;
xx.test();
/*Intent refresh = new Intent(PinnedSectionListActivity._context, PinnedSectionListActivity.class);
PinnedSectionListActivity._context.startActivity(refresh);*/
}
// get expected adapter or fail fast
ListAdapter adapter = getAdapter();
if (adapter == null || visibleItemCount == 0) return; // nothing to do
final boolean isFirstVisibleItemSection =
isItemViewTypePinned(adapter, adapter.getItemViewType(firstVisibleItem));
if (isFirstVisibleItemSection) {
View sectionView = getChildAt(0);
if (sectionView.getTop() == getPaddingTop()) { // view sticks to the top, no need for pinned shadow
destroyPinnedShadow();
} else { // section doesn't stick to the top, make sure we have a pinned shadow
ensureShadowForPosition(firstVisibleItem, firstVisibleItem, visibleItemCount);
}
} else { // section is not at the first visible position
int sectionPosition = findCurrentSectionPosition(firstVisibleItem);
if (sectionPosition > -1) { // we have section position
ensureShadowForPosition(sectionPosition, firstVisibleItem, visibleItemCount);
} else { // there is no section for the first visible item, destroy shadow
destroyPinnedShadow();
}
}
};
};
/** Default change observer. */
private final DataSetObserver mDataSetObserver = new DataSetObserver() {
#Override public void onChanged() {
recreatePinnedShadow();
};
#Override public void onInvalidated() {
recreatePinnedShadow();
}
};
//-- constructors
public PinnedSectionListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public PinnedSectionListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
private void initView() {
setOnScrollListener(mOnScrollListener);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
initShadow(true);
}
//-- public API methods
public void setShadowVisible(boolean visible) {
initShadow(visible);
if (mPinnedSection != null) {
View v = mPinnedSection.view;
invalidate(v.getLeft(), v.getTop(), v.getRight(), v.getBottom() + mShadowHeight);
}
}
//-- pinned section drawing methods
public void initShadow(boolean visible) {
if (visible) {
if (mShadowDrawable == null) {
mShadowDrawable = new GradientDrawable(Orientation.TOP_BOTTOM,
new int[] { Color.parseColor("#ffa0a0a0"), Color.parseColor("#50a0a0a0"), Color.parseColor("#00a0a0a0")});
mShadowHeight = (int) (8 * getResources().getDisplayMetrics().density);
}
} else {
if (mShadowDrawable != null) {
mShadowDrawable = null;
mShadowHeight = 0;
}
}
}
/** Create shadow wrapper with a pinned view for a view at given position */
void createPinnedShadow(int position) {
// try to recycle shadow
PinnedSection pinnedShadow = mRecycleSection;
mRecycleSection = null;
// create new shadow, if needed
if (pinnedShadow == null) pinnedShadow = new PinnedSection();
// request new view using recycled view, if such
View pinnedView = getAdapter().getView(position, pinnedShadow.view, PinnedSectionListView.this);
// read layout parameters
LayoutParams layoutParams = (LayoutParams) pinnedView.getLayoutParams();
if (layoutParams == null) {
layoutParams = (LayoutParams) generateDefaultLayoutParams();
pinnedView.setLayoutParams(layoutParams);
}
int heightMode = MeasureSpec.getMode(layoutParams.height);
int heightSize = MeasureSpec.getSize(layoutParams.height);
if (heightMode == MeasureSpec.UNSPECIFIED) heightMode = MeasureSpec.EXACTLY;
int maxHeight = getHeight() - getListPaddingTop() - getListPaddingBottom();
if (heightSize > maxHeight) heightSize = maxHeight;
// measure & layout
int ws = MeasureSpec.makeMeasureSpec(getWidth() - getListPaddingLeft() - getListPaddingRight(), MeasureSpec.EXACTLY);
int hs = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
pinnedView.measure(ws, hs);
pinnedView.layout(0, 0, pinnedView.getMeasuredWidth(), pinnedView.getMeasuredHeight());
mTranslateY = 0;
// initialize pinned shadow
pinnedShadow.view = pinnedView;
pinnedShadow.position = position;
pinnedShadow.id = getAdapter().getItemId(position);
// store pinned shadow
mPinnedSection = pinnedShadow;
}
/** Destroy shadow wrapper for currently pinned view */
void destroyPinnedShadow() {
if (mPinnedSection != null) {
// keep shadow for being recycled later
mRecycleSection = mPinnedSection;
mPinnedSection = null;
}
}
/** Makes sure we have an actual pinned shadow for given position. */
void ensureShadowForPosition(int sectionPosition, int firstVisibleItem, int visibleItemCount) {
if (visibleItemCount < 2) { // no need for creating shadow at all, we have a single visible item
destroyPinnedShadow();
return;
}
if (mPinnedSection != null
&& mPinnedSection.position != sectionPosition) { // invalidate shadow, if required
destroyPinnedShadow();
}
if (mPinnedSection == null) { // create shadow, if empty
createPinnedShadow(sectionPosition);
}
// align shadow according to next section position, if needed
int nextPosition = sectionPosition + 1;
if (nextPosition < getCount()) {
int nextSectionPosition = findFirstVisibleSectionPosition(nextPosition,
visibleItemCount - (nextPosition - firstVisibleItem));
if (nextSectionPosition > -1) {
View nextSectionView = getChildAt(nextSectionPosition - firstVisibleItem);
final int bottom = mPinnedSection.view.getBottom() + getPaddingTop();
mSectionsDistanceY = nextSectionView.getTop() - bottom;
if (mSectionsDistanceY < 0) {
// next section overlaps pinned shadow, move it up
mTranslateY = mSectionsDistanceY;
} else {
// next section does not overlap with pinned, stick to top
mTranslateY = 0;
}
} else {
// no other sections are visible, stick to top
mTranslateY = 0;
mSectionsDistanceY = Integer.MAX_VALUE;
}
}
}
int findFirstVisibleSectionPosition(int firstVisibleItem, int visibleItemCount) {
ListAdapter adapter = getAdapter();
int adapterDataCount = adapter.getCount();
if (getLastVisiblePosition() >= adapterDataCount) return -1; // dataset has changed, no candidate
if (firstVisibleItem+visibleItemCount >= adapterDataCount){//added to prevent index Outofbound (in case)
visibleItemCount = adapterDataCount-firstVisibleItem;
}
for (int childIndex = 0; childIndex < visibleItemCount; childIndex++) {
int position = firstVisibleItem + childIndex;
int viewType = adapter.getItemViewType(position);
if (isItemViewTypePinned(adapter, viewType)) return position;
}
return -1;
}
int findCurrentSectionPosition(int fromPosition) {
ListAdapter adapter = getAdapter();
if (fromPosition >= adapter.getCount()) return -1; // dataset has changed, no candidate
if (adapter instanceof SectionIndexer) {
// try fast way by asking section indexer
SectionIndexer indexer = (SectionIndexer) adapter;
int sectionPosition = indexer.getSectionForPosition(fromPosition);
int itemPosition = indexer.getPositionForSection(sectionPosition);
int typeView = adapter.getItemViewType(itemPosition);
if (isItemViewTypePinned(adapter, typeView)) {
return itemPosition;
} // else, no luck
}
// try slow way by looking through to the next section item above
for (int position=fromPosition; position>=0; position--) {
int viewType = adapter.getItemViewType(position);
if (isItemViewTypePinned(adapter, viewType)) return position;
}
return -1; // no candidate found
}
void recreatePinnedShadow() {
destroyPinnedShadow();
ListAdapter adapter = getAdapter();
if (adapter != null && adapter.getCount() > 0) {
int firstVisiblePosition = getFirstVisiblePosition();
int sectionPosition = findCurrentSectionPosition(firstVisiblePosition);
if (sectionPosition == -1) return; // no views to pin, exit
ensureShadowForPosition(sectionPosition,
firstVisiblePosition, getLastVisiblePosition() - firstVisiblePosition);
}
}
#Override
public void setOnScrollListener(OnScrollListener listener) {
if (listener == mOnScrollListener) {
super.setOnScrollListener(listener);
} else {
mDelegateOnScrollListener = listener;
}
}
#Override
public void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
post(new Runnable() {
#Override public void run() { // restore pinned view after configuration change
recreatePinnedShadow();
}
});
}
#Override
public void setAdapter(ListAdapter adapter) {
// assert adapter in debug mode
if (BuildConfig.DEBUG && adapter != null) {
if (!(adapter instanceof PinnedSectionListAdapter))
throw new IllegalArgumentException("Does your adapter implement PinnedSectionListAdapter?");
if (adapter.getViewTypeCount() < 2)
throw new IllegalArgumentException("Does your adapter handle at least two types" +
" of views in getViewTypeCount() method: items and sections?");
}
// unregister observer at old adapter and register on new one
ListAdapter oldAdapter = getAdapter();
if (oldAdapter != null) oldAdapter.unregisterDataSetObserver(mDataSetObserver);
if (adapter != null) adapter.registerDataSetObserver(mDataSetObserver);
// destroy pinned shadow, if new adapter is not same as old one
if (oldAdapter != adapter) destroyPinnedShadow();
super.setAdapter(adapter);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mPinnedSection != null) {
int parentWidth = r - l - getPaddingLeft() - getPaddingRight();
int shadowWidth = mPinnedSection.view.getWidth();
if (parentWidth != shadowWidth) {
recreatePinnedShadow();
}
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mPinnedSection != null) {
// prepare variables
int pLeft = getListPaddingLeft();
int pTop = getListPaddingTop();
View view = mPinnedSection.view;
// draw child
canvas.save();
int clipHeight = view.getHeight() +
(mShadowDrawable == null ? 0 : Math.min(mShadowHeight, mSectionsDistanceY));
canvas.clipRect(pLeft, pTop, pLeft + view.getWidth(), pTop + clipHeight);
canvas.translate(pLeft, pTop + mTranslateY);
drawChild(canvas, mPinnedSection.view, getDrawingTime());
if (mShadowDrawable != null && mSectionsDistanceY > 0) {
mShadowDrawable.setBounds(mPinnedSection.view.getLeft(),
mPinnedSection.view.getBottom(),
mPinnedSection.view.getRight(),
mPinnedSection.view.getBottom() + mShadowHeight);
mShadowDrawable.draw(canvas);
}
canvas.restore();
}
}
//-- touch handling methods
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
final int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN
&& mTouchTarget == null
&& mPinnedSection != null
&& isPinnedViewTouched(mPinnedSection.view, x, y)) { // create touch target
// user touched pinned view
mTouchTarget = mPinnedSection.view;
mTouchPoint.x = x;
mTouchPoint.y = y;
// copy down event for eventually be used later
mDownEvent = MotionEvent.obtain(ev);
}
if (mTouchTarget != null) {
if (isPinnedViewTouched(mTouchTarget, x, y)) { // forward event to pinned view
mTouchTarget.dispatchTouchEvent(ev);
}
if (action == MotionEvent.ACTION_UP) { // perform onClick on pinned view
super.dispatchTouchEvent(ev);
performPinnedItemClick();
clearTouchTarget();
} else if (action == MotionEvent.ACTION_CANCEL) { // cancel
clearTouchTarget();
} else if (action == MotionEvent.ACTION_MOVE) {
if (Math.abs(y - mTouchPoint.y) > mTouchSlop) {
// cancel sequence on touch target
MotionEvent event = MotionEvent.obtain(ev);
event.setAction(MotionEvent.ACTION_CANCEL);
mTouchTarget.dispatchTouchEvent(event);
event.recycle();
// provide correct sequence to super class for further handling
super.dispatchTouchEvent(mDownEvent);
super.dispatchTouchEvent(ev);
clearTouchTarget();
}
}
return true;
}
// call super if this was not our pinned view
return super.dispatchTouchEvent(ev);
}
private boolean isPinnedViewTouched(View view, float x, float y) {
view.getHitRect(mTouchRect);
// by taping top or bottom padding, the list performs on click on a border item.
// we don't add top padding here to keep behavior consistent.
mTouchRect.top += mTranslateY;
mTouchRect.bottom += mTranslateY + getPaddingTop();
mTouchRect.left += getPaddingLeft();
mTouchRect.right -= getPaddingRight();
return mTouchRect.contains((int)x, (int)y);
}
private void clearTouchTarget() {
mTouchTarget = null;
if (mDownEvent != null) {
mDownEvent.recycle();
mDownEvent = null;
}
}
private boolean performPinnedItemClick() {
if (mPinnedSection == null) return false;
OnItemClickListener listener = getOnItemClickListener();
if (listener != null && getAdapter().isEnabled(mPinnedSection.position)) {
View view = mPinnedSection.view;
playSoundEffect(SoundEffectConstants.CLICK);
if (view != null) {
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
}
listener.onItemClick(this, view, mPinnedSection.position, mPinnedSection.id);
return true;
}
return false;
}
public static boolean isItemViewTypePinned(ListAdapter adapter, int viewType) {
if (adapter instanceof HeaderViewListAdapter) {
adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter();
}
return ((PinnedSectionListAdapter) adapter).isItemViewTypePinned(viewType);
}
}
For second time get me error here :
SimpleAdapter adapt = new SimpleAdapter(_context, result, resultT, count, android.R.layout.simple_list_item_1, android.R.id.text1);
setListAdapter(adapt);
Your not using PinnedSectionListActivity current reference properly,If you wan to access your current public function inside any other class then you have to pass your current activity reference :
Declaration
private Context context;
Get current activity reference
public PinnedSectionListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
initView();
}
public PinnedSectionListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
initView();
}
Use current activity reference in onScroll() :
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if(firstVisibleItem+visibleItemCount == totalItemCount && totalItemCount!=0)
{
if(context!=null){
((PinnedSectionListActivity)context).cleans();
}
}else {
// get expected adapter or fail fast
ListAdapter adapter = getAdapter();
if (adapter == null || visibleItemCount == 0) return; // nothing to do
final boolean isFirstVisibleItemSection = isItemViewTypePinned(adapter, adapter.getItemViewType(firstVisibleItem));
if (isFirstVisibleItemSection) {
View sectionView = getChildAt(0);
if (sectionView.getTop() == getPaddingTop()) { // view sticks to the top, no need for pinned shadow
destroyPinnedShadow();
} else { // section doesn't stick to the top, make sure we have a pinned shadow
ensureShadowForPosition(firstVisibleItem, firstVisibleItem, visibleItemCount);
}
} else { // section is not at the first visible position
int sectionPosition = findCurrentSectionPosition(firstVisibleItem);
if (sectionPosition > -1) { // we have section position
ensureShadowForPosition(sectionPosition, firstVisibleItem, visibleItemCount);
} else { // there is no section for the first visible item, destroy shadow
destroyPinnedShadow();
}
}
}
};
PinnedSectionListView full activity code :
public class PinnedSectionListView extends ListView {
//-- inner classes
/** List adapter to be implemented for being used with PinnedSectionListView adapter. */
public static interface PinnedSectionListAdapter extends ListAdapter {
/** This method shall return 'true' if views of given type has to be pinned. */
boolean isItemViewTypePinned(int viewType);
}
/** Wrapper class for pinned section view and its position in the list. */
static class PinnedSection {
public View view;
public int position;
public long id;
}
//-- class fields
// fields used for handling touch events
private final Rect mTouchRect = new Rect();
private final PointF mTouchPoint = new PointF();
private int mTouchSlop;
private View mTouchTarget;
private MotionEvent mDownEvent;
// fields used for drawing shadow under a pinned section
private GradientDrawable mShadowDrawable;
private int mSectionsDistanceY;
private int mShadowHeight;
/** Delegating listener, can be null. */
OnScrollListener mDelegateOnScrollListener;
/** Shadow for being recycled, can be null. */
PinnedSection mRecycleSection;
/** shadow instance with a pinned view, can be null. */
PinnedSection mPinnedSection;
/** Pinned view Y-translation. We use it to stick pinned view to the next section. */
int mTranslateY;
private Context context;
/** Scroll listener which does the magic */
private final OnScrollListener mOnScrollListener = new OnScrollListener() {
#Override public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScrollStateChanged(view, scrollState);
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if(firstVisibleItem+visibleItemCount == totalItemCount && totalItemCount!=0)
{
if(context!=null){
((PinnedSectionListActivity)context).cleans();
}
}else {
// get expected adapter or fail fast
ListAdapter adapter = getAdapter();
if (adapter == null || visibleItemCount == 0) return; // nothing to do
final boolean isFirstVisibleItemSection = isItemViewTypePinned(adapter, adapter.getItemViewType(firstVisibleItem));
if (isFirstVisibleItemSection) {
View sectionView = getChildAt(0);
if (sectionView.getTop() == getPaddingTop()) { // view sticks to the top, no need for pinned shadow
destroyPinnedShadow();
} else { // section doesn't stick to the top, make sure we have a pinned shadow
ensureShadowForPosition(firstVisibleItem, firstVisibleItem, visibleItemCount);
}
} else { // section is not at the first visible position
int sectionPosition = findCurrentSectionPosition(firstVisibleItem);
if (sectionPosition > -1) { // we have section position
ensureShadowForPosition(sectionPosition, firstVisibleItem, visibleItemCount);
} else { // there is no section for the first visible item, destroy shadow
destroyPinnedShadow();
}
}
}
};
};
/** Default change observer. */
private final DataSetObserver mDataSetObserver = new DataSetObserver() {
#Override public void onChanged() {
recreatePinnedShadow();
};
#Override public void onInvalidated() {
recreatePinnedShadow();
}
};
//-- constructors
public PinnedSectionListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
initView();
}
public PinnedSectionListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
initView();
}
private void initView() {
setOnScrollListener(mOnScrollListener);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
initShadow(true);
}
//-- public API methods
public void setShadowVisible(boolean visible) {
initShadow(visible);
if (mPinnedSection != null) {
View v = mPinnedSection.view;
invalidate(v.getLeft(), v.getTop(), v.getRight(), v.getBottom() + mShadowHeight);
}
}
//-- pinned section drawing methods
public void initShadow(boolean visible) {
if (visible) {
if (mShadowDrawable == null) {
mShadowDrawable = new GradientDrawable(Orientation.TOP_BOTTOM,
new int[] { Color.parseColor("#ffa0a0a0"), Color.parseColor("#50a0a0a0"), Color.parseColor("#00a0a0a0")});
mShadowHeight = (int) (8 * getResources().getDisplayMetrics().density);
}
} else {
if (mShadowDrawable != null) {
mShadowDrawable = null;
mShadowHeight = 0;
}
}
}
/** Create shadow wrapper with a pinned view for a view at given position */
void createPinnedShadow(int position) {
// try to recycle shadow
PinnedSection pinnedShadow = mRecycleSection;
mRecycleSection = null;
// create new shadow, if needed
if (pinnedShadow == null) pinnedShadow = new PinnedSection();
// request new view using recycled view, if such
View pinnedView = getAdapter().getView(position, pinnedShadow.view, PinnedSectionListView.this);
// read layout parameters
LayoutParams layoutParams = (LayoutParams) pinnedView.getLayoutParams();
if (layoutParams == null) {
layoutParams = (LayoutParams) generateDefaultLayoutParams();
pinnedView.setLayoutParams(layoutParams);
}
int heightMode = MeasureSpec.getMode(layoutParams.height);
int heightSize = MeasureSpec.getSize(layoutParams.height);
if (heightMode == MeasureSpec.UNSPECIFIED) heightMode = MeasureSpec.EXACTLY;
int maxHeight = getHeight() - getListPaddingTop() - getListPaddingBottom();
if (heightSize > maxHeight) heightSize = maxHeight;
// measure & layout
int ws = MeasureSpec.makeMeasureSpec(getWidth() - getListPaddingLeft() - getListPaddingRight(), MeasureSpec.EXACTLY);
int hs = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
pinnedView.measure(ws, hs);
pinnedView.layout(0, 0, pinnedView.getMeasuredWidth(), pinnedView.getMeasuredHeight());
mTranslateY = 0;
// initialize pinned shadow
pinnedShadow.view = pinnedView;
pinnedShadow.position = position;
pinnedShadow.id = getAdapter().getItemId(position);
// store pinned shadow
mPinnedSection = pinnedShadow;
}
/** Destroy shadow wrapper for currently pinned view */
void destroyPinnedShadow() {
if (mPinnedSection != null) {
// keep shadow for being recycled later
mRecycleSection = mPinnedSection;
mPinnedSection = null;
}
}
/** Makes sure we have an actual pinned shadow for given position. */
void ensureShadowForPosition(int sectionPosition, int firstVisibleItem, int visibleItemCount) {
if (visibleItemCount < 2) { // no need for creating shadow at all, we have a single visible item
destroyPinnedShadow();
return;
}
if (mPinnedSection != null
&& mPinnedSection.position != sectionPosition) { // invalidate shadow, if required
destroyPinnedShadow();
}
if (mPinnedSection == null) { // create shadow, if empty
createPinnedShadow(sectionPosition);
}
// align shadow according to next section position, if needed
int nextPosition = sectionPosition + 1;
if (nextPosition < getCount()) {
int nextSectionPosition = findFirstVisibleSectionPosition(nextPosition,
visibleItemCount - (nextPosition - firstVisibleItem));
if (nextSectionPosition > -1) {
View nextSectionView = getChildAt(nextSectionPosition - firstVisibleItem);
final int bottom = mPinnedSection.view.getBottom() + getPaddingTop();
mSectionsDistanceY = nextSectionView.getTop() - bottom;
if (mSectionsDistanceY < 0) {
// next section overlaps pinned shadow, move it up
mTranslateY = mSectionsDistanceY;
} else {
// next section does not overlap with pinned, stick to top
mTranslateY = 0;
}
} else {
// no other sections are visible, stick to top
mTranslateY = 0;
mSectionsDistanceY = Integer.MAX_VALUE;
}
}
}
int findFirstVisibleSectionPosition(int firstVisibleItem, int visibleItemCount) {
ListAdapter adapter = getAdapter();
int adapterDataCount = adapter.getCount();
if (getLastVisiblePosition() >= adapterDataCount) return -1; // dataset has changed, no candidate
if (firstVisibleItem+visibleItemCount >= adapterDataCount){//added to prevent index Outofbound (in case)
visibleItemCount = adapterDataCount-firstVisibleItem;
}
for (int childIndex = 0; childIndex < visibleItemCount; childIndex++) {
int position = firstVisibleItem + childIndex;
int viewType = adapter.getItemViewType(position);
if (isItemViewTypePinned(adapter, viewType)) return position;
}
return -1;
}
int findCurrentSectionPosition(int fromPosition) {
ListAdapter adapter = getAdapter();
if (fromPosition >= adapter.getCount()) return -1; // dataset has changed, no candidate
if (adapter instanceof SectionIndexer) {
// try fast way by asking section indexer
SectionIndexer indexer = (SectionIndexer) adapter;
int sectionPosition = indexer.getSectionForPosition(fromPosition);
int itemPosition = indexer.getPositionForSection(sectionPosition);
int typeView = adapter.getItemViewType(itemPosition);
if (isItemViewTypePinned(adapter, typeView)) {
return itemPosition;
} // else, no luck
}
// try slow way by looking through to the next section item above
for (int position=fromPosition; position>=0; position--) {
int viewType = adapter.getItemViewType(position);
if (isItemViewTypePinned(adapter, viewType)) return position;
}
return -1; // no candidate found
}
void recreatePinnedShadow() {
destroyPinnedShadow();
ListAdapter adapter = getAdapter();
if (adapter != null && adapter.getCount() > 0) {
int firstVisiblePosition = getFirstVisiblePosition();
int sectionPosition = findCurrentSectionPosition(firstVisiblePosition);
if (sectionPosition == -1) return; // no views to pin, exit
ensureShadowForPosition(sectionPosition,
firstVisiblePosition, getLastVisiblePosition() - firstVisiblePosition);
}
}
#Override
public void setOnScrollListener(OnScrollListener listener) {
if (listener == mOnScrollListener) {
super.setOnScrollListener(listener);
} else {
mDelegateOnScrollListener = listener;
}
}
#Override
public void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
post(new Runnable() {
#Override public void run() { // restore pinned view after configuration change
recreatePinnedShadow();
}
});
}
#Override
public void setAdapter(ListAdapter adapter) {
// assert adapter in debug mode
if (BuildConfig.DEBUG && adapter != null) {
if (!(adapter instanceof PinnedSectionListAdapter))
throw new IllegalArgumentException("Does your adapter implement PinnedSectionListAdapter?");
if (adapter.getViewTypeCount() < 2)
throw new IllegalArgumentException("Does your adapter handle at least two types" +
" of views in getViewTypeCount() method: items and sections?");
}
// unregister observer at old adapter and register on new one
ListAdapter oldAdapter = getAdapter();
if (oldAdapter != null) oldAdapter.unregisterDataSetObserver(mDataSetObserver);
if (adapter != null) adapter.registerDataSetObserver(mDataSetObserver);
// destroy pinned shadow, if new adapter is not same as old one
if (oldAdapter != adapter) destroyPinnedShadow();
super.setAdapter(adapter);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mPinnedSection != null) {
int parentWidth = r - l - getPaddingLeft() - getPaddingRight();
int shadowWidth = mPinnedSection.view.getWidth();
if (parentWidth != shadowWidth) {
recreatePinnedShadow();
}
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mPinnedSection != null) {
// prepare variables
int pLeft = getListPaddingLeft();
int pTop = getListPaddingTop();
View view = mPinnedSection.view;
// draw child
canvas.save();
int clipHeight = view.getHeight() +
(mShadowDrawable == null ? 0 : Math.min(mShadowHeight, mSectionsDistanceY));
canvas.clipRect(pLeft, pTop, pLeft + view.getWidth(), pTop + clipHeight);
canvas.translate(pLeft, pTop + mTranslateY);
drawChild(canvas, mPinnedSection.view, getDrawingTime());
if (mShadowDrawable != null && mSectionsDistanceY > 0) {
mShadowDrawable.setBounds(mPinnedSection.view.getLeft(),
mPinnedSection.view.getBottom(),
mPinnedSection.view.getRight(),
mPinnedSection.view.getBottom() + mShadowHeight);
mShadowDrawable.draw(canvas);
}
canvas.restore();
}
}
//-- touch handling methods
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
final int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN
&& mTouchTarget == null
&& mPinnedSection != null
&& isPinnedViewTouched(mPinnedSection.view, x, y)) { // create touch target
// user touched pinned view
mTouchTarget = mPinnedSection.view;
mTouchPoint.x = x;
mTouchPoint.y = y;
// copy down event for eventually be used later
mDownEvent = MotionEvent.obtain(ev);
}
if (mTouchTarget != null) {
if (isPinnedViewTouched(mTouchTarget, x, y)) { // forward event to pinned view
mTouchTarget.dispatchTouchEvent(ev);
}
if (action == MotionEvent.ACTION_UP) { // perform onClick on pinned view
super.dispatchTouchEvent(ev);
performPinnedItemClick();
clearTouchTarget();
} else if (action == MotionEvent.ACTION_CANCEL) { // cancel
clearTouchTarget();
} else if (action == MotionEvent.ACTION_MOVE) {
if (Math.abs(y - mTouchPoint.y) > mTouchSlop) {
// cancel sequence on touch target
MotionEvent event = MotionEvent.obtain(ev);
event.setAction(MotionEvent.ACTION_CANCEL);
mTouchTarget.dispatchTouchEvent(event);
event.recycle();
// provide correct sequence to super class for further handling
super.dispatchTouchEvent(mDownEvent);
super.dispatchTouchEvent(ev);
clearTouchTarget();
}
}
return true;
}
// call super if this was not our pinned view
return super.dispatchTouchEvent(ev);
}
private boolean isPinnedViewTouched(View view, float x, float y) {
view.getHitRect(mTouchRect);
// by taping top or bottom padding, the list performs on click on a border item.
// we don't add top padding here to keep behavior consistent.
mTouchRect.top += mTranslateY;
mTouchRect.bottom += mTranslateY + getPaddingTop();
mTouchRect.left += getPaddingLeft();
mTouchRect.right -= getPaddingRight();
return mTouchRect.contains((int)x, (int)y);
}
private void clearTouchTarget() {
mTouchTarget = null;
if (mDownEvent != null) {
mDownEvent.recycle();
mDownEvent = null;
}
}
private boolean performPinnedItemClick() {
if (mPinnedSection == null) return false;
OnItemClickListener listener = getOnItemClickListener();
if (listener != null && getAdapter().isEnabled(mPinnedSection.position)) {
View view = mPinnedSection.view;
playSoundEffect(SoundEffectConstants.CLICK);
if (view != null) {
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
}
listener.onItemClick(this, view, mPinnedSection.position, mPinnedSection.id);
return true;
}
return false;
}
public static boolean isItemViewTypePinned(ListAdapter adapter, int viewType) {
if (adapter instanceof HeaderViewListAdapter) {
adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter();
}
return ((PinnedSectionListAdapter) adapter).isItemViewTypePinned(viewType);
}
}

Is it possible to make Imageview with checked and unchecked like radiobutton?

I am having nearly 500+ images in Imageview inside Horizontalscrollview. If i am selecting an image then I am marking it as selected. If I am selecting any other images in the view, it should be un-select and newly clicked image should have to be selected. How could I can achieve it?
for (int i = 0; i < Home.arr_category_item_list.size(); i++) {
ImageView circleImageView = new ImageView(getActivity());
imageLoader.get(Home.arr_category_item_list.get(i).get(Variables.EST_CATEGORY_ITEM_IMAGE), ImageLoader.getImageListener(circleImageView, R.drawable.defaultimage, R.drawable.defaultimage));
circleImageView.setTag(Integer.parseInt(Home.arr_category_item_list.get(i).get(Variables.EST_CATEGORY_ITEM_ID)));
circleImageView.setLayoutParams(params);
lnr_category_item.addView(circleImageView);
}
Please check the image attached. At the bottom of the screen there is an Image view. So user will have option to select only one image at a time.
Now this is some really old code, hope it still works.
Note: there may be a thing or two missing, but you can get the idea from this implementation
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import yourpackage.R;
//Creataed by Bojan Kseneman on 14.8.2013
public class CustomCheckBox extends ImageView {
private boolean isChecked;
private boolean isImageShown;
private boolean useCustomClickListener;
//private String android_xmlns = "http://schemas.android.com/apk/res/android";
private String app_xmlns;
private int checkboxOnResID;
private int checkboxOffResID;
private int checkboxDisabledOnResID;
private int checkboxDisabledOffResID;
//private int imageHeight;
//private int imageWidth;
public CustomCheckBox(Context context, AttributeSet attrs) {
super(context, attrs);
app_xmlns = new StringBuilder("http://schemas.android.com/apk/res/" + context.getPackageName()).toString();
init(attrs);
}
private void init(AttributeSet attrs) {
checkboxOnResID = attrs.getAttributeResourceValue(app_xmlns, "resourceChecked", R.drawable.round_checkbox_on);
checkboxOffResID = attrs.getAttributeResourceValue(app_xmlns, "resourceNotChecked", R.drawable.round_checkbox_off);
checkboxDisabledOnResID = attrs.getAttributeResourceValue(app_xmlns, "resourceDisabledOn", R.drawable.round_checkbox_off);
checkboxDisabledOffResID = attrs.getAttributeResourceValue(app_xmlns, "resourceDisabledOff", R.drawable.round_checkbox_off);
useCustomClickListener = attrs.getAttributeBooleanValue(app_xmlns, "customClickEvent", false);
if (useCustomClickListener)
this.setOnTouchListener(new CboxTouchListener());
else {
this.setOnTouchListener(new NormalClickListener());
}
if (!hasOnClickListener()) {
/**
* assign a new onClick listener so we get desired onClick sound
* effect (because we call it) this is opposite to how android
* behaves, where you don't hear the sound if there is not
* onClickListener assigned
*/
this.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
}
private boolean hasOnClickListener() {
try {
if (android.os.Build.VERSION.SDK_INT >= 14) {
//the information is inside ListenerInfo
java.lang.reflect.Field listenerInfoField = null;
listenerInfoField = Class.forName("android.view.View").getDeclaredField("mListenerInfo");
if (listenerInfoField != null)
listenerInfoField.setAccessible(true);
Object mOnClickListener = null;
mOnClickListener = listenerInfoField.get(this); //get from view object, in this case this is this
// get the field mOnClickListener, that holds the listener and cast it to a listener
java.lang.reflect.Field listenerField = null;
listenerField = Class.forName("android.view.View$ListenerInfo").getDeclaredField("mOnClickListener");
//View.OnClickListener myListener = (View.OnClickListener) listenerField.get(myLiObject);
return (listenerField.get(mOnClickListener) != null);
}
else {
//directly in View
java.lang.reflect.Field f = Class.forName("android.view.View").getDeclaredField("mOnClickListener");
return (f.get(this) != null);
}
}
catch (Exception e) {
return false;
}
}
// private void setScaledDownImage(int resID) {
// this.setImageBitmap(CommonMethods.decodeSampledBitmapFromResource(getContext(), resID, imageWidth, imageHeight));
// }
public boolean isChecked() {
return (isChecked && this.isEnabled());
}
public void setChecked(boolean isChecked) {
if (this.isEnabled())
setCheckedIgnoreEnabled(isChecked);
else
this.setEnabled(false);
}
public void setCheckedIgnoreEnabled(boolean isChecked) {
if ((this.isChecked != isChecked) || !isImageShown) {
this.isChecked = isChecked;
if (isChecked)
setImageResource(checkboxOnResID);
else
setImageResource(checkboxOffResID);
}
}
#Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if (enabled)
setChecked(isChecked);
else {
int resID = isChecked ? checkboxDisabledOnResID : checkboxDisabledOffResID;
setImageResource(resID);
}
}
public void setCheckedAndEnabled(boolean isChecked, boolean isEnabled) {
setCheckedIgnoreEnabled(isChecked);
setEnabled(isEnabled);
}
public void toggle() {
setChecked(!isChecked);
}
public void toggleWithClick() {
toggle();
this.performClick();
}
public void toggleWithSilentClick() {
//v.playSoundEffect(android.view.SoundEffectConstants.CLICK);
boolean currentState = this.isSoundEffectsEnabled();
this.setSoundEffectsEnabled(false);
toggleWithClick();
this.setSoundEffectsEnabled(currentState);
}
private class CboxTouchListener extends ImageBoundClickListener {
#Override
public void doSomething() {
super.doSomething();
setChecked(!isChecked);
CustomCheckBox.this.performClick();
}
}
private class NormalClickListener extends MyOnClickListener {
#Override
public void doSomething() {
super.doSomething();
setChecked(!isChecked);
CustomCheckBox.this.performClick();
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//this.imageWidth = MeasureSpec.getSize(widthMeasureSpec);
//this.imageHeight = MeasureSpec.getSize(heightMeasureSpec);
if (!isImageShown) {
setChecked(isChecked);
isImageShown = !isImageShown;
}
}
}
I used this click listener, since I was using some round images and I didn't want to trigger the click events when the user clicked on the transparent part of the image.
public abstract class ImageBoundClickListener implements android.view.View.OnTouchListener {
private String TAG = "ImageBoundsTouchListener";
private boolean shouldTriggerAction = false;
Rect allowedArea;
#Override
public boolean onTouch(View v, MotionEvent event) {
//let's us detect only clicks on the part where actual image is and not where the ImageView is
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float[] eventXY = new float[] { event.getX(), event.getY() };
allowedArea = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
android.graphics.Matrix invertMatrix = new android.graphics.Matrix();
ImageView iv = (ImageView) v;
iv.getImageMatrix().invert(invertMatrix);
invertMatrix.mapPoints(eventXY);
int x = (int) eventXY[0];
int y = (int) eventXY[1];
Drawable imgDrawable = iv.getDrawable();
Bitmap bitmap = ((BitmapDrawable) imgDrawable).getBitmap();
//Limit x,y within the bitmap
if (x < 0)
x = 0;
else if (x > (bitmap.getWidth() - 1))
x = bitmap.getWidth() - 1;
if (y < 0)
y = 0;
else if (y > bitmap.getHeight() - 1)
y = bitmap.getHeight() - 1;
int touchedRGB = bitmap.getPixel(x, y);
//is transparent?
shouldTriggerAction = (touchedRGB == 0) ? false : true;
return true;
case MotionEvent.ACTION_MOVE:
if (!allowedArea.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY()))
//the user went out of the user area
shouldTriggerAction = false;
return true;
case MotionEvent.ACTION_UP:
//finger is no longer on screen
if (shouldTriggerAction)
doSomething();
return true;
default:
return false;
}
}
public void doSomething() {
}
}

How to add animation while actionbar are changing their content with Appcompat?

I'm looking at Google's Material Design guidelines and I want add animated action bar. My goal is do something like this:
How can I add transition for action bar's content? I'm using Appcompat to keep backward compatibility.
Update:
I've created an open source library that provides transition/animation support to both View and MenuItem:
MenuItem transition
View transition
Instructions:
On Android Studio, add the code below to Gradle dependencies:
compile 'com.github.kaichunlin.transition:core:0.8.1'
Sample code with explanations:
protected void onCreate(Bundle savedInstanceState) {
//...
//standard onCreate() stuff that creates set configs toolbar, mDrawerLayout & mDrawerToggle
//Use the appropriate adapter that extends MenuBaseAdapter:
DrawerListenerAdapter mDrawerListenerAdapter = new DrawerListenerAdapter(mDrawerToggle, R.id.drawerList);
mDrawerListenerAdapter.setDrawerLayout(mDrawerLayout);
//Add desired transition to the adapter, MenuItemTransitionBuilder is used to build the transition:
//Creates a shared configuration that: applies alpha, the transition effect is applied in a cascading manner (v.s. simultaneously), MenuItems will resets to enabled when transiting, and invalidates menu on transition completion
MenuItemTransitionBuilder builder = MenuItemTransitionBuilder.transit(toolbar).alpha(1f, 0.5f).scale(1f, 0f).cascade(0.3f).visibleOnStartAnimation(true).invalidateOptionOnStopTransition(this, true);
MenuItemTransition mShrinkClose = builder.translationX(0, 30).build();
MenuItemTransition mShrinkOpen = builder.reverse().translationX(0, 30).build();
mDrawerListenerAdapter.setupOptions(this, new MenuOptionConfiguration(mShrinkOpen, R.menu.drawer), new MenuOptionConfiguration(mShrinkClose, R.menu.main));
}
//Let the adapter manage the creation of options menu:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
mDrawerListenerAdapter.onCreateOptionsMenu(this, menu);
return super.onCreateOptionsMenu(menu);
}
Source of the activity implementing the above is here, and a demo app here.
Originally Accepted Answer:
Here's a solution that's more versatile and is exactly how the MenuItem fade-out of Google Drive, Google Docs, Google Sheets, and Google Slides work.
The advantage is that when the user slide in from the left edge of the screen to open the drawer manually, or slide right when the drawer is opened to close it, the animation state is integrated with how the drawer is being opened/closed.
ProgressAnimator.java: This is the meat of the implementation, it translates a float based progression value (0f~1f) into a value that Android Animator understands.
public class ProgressAnimator implements TimeAnimator.TimeListener {
private final List<AnimationControl> animationControls = new ArrayList<>();
private final MenuItem mMenuItem; //TODO shouldn't be here, add animation end listener
private final ImageView mImageView;
private final TimeAnimator mTimeAnim;
private final AnimatorSet mInternalAnimSet;
public ProgressAnimator(Context context, MenuItem mMenuItem) {
if (mMenuItem == null) {
mImageView = null;
} else {
mImageView = (ImageView) LayoutInflater.from(context).inflate(R.layout.menu_animation, null).findViewById(R.id.menu_animation);
mImageView.setImageDrawable(mMenuItem.getIcon());
}
this.mMenuItem = mMenuItem;
this.mInternalAnimSet = new AnimatorSet();
mTimeAnim = new TimeAnimator();
mTimeAnim.setTimeListener(this);
}
public void addAnimatorSet(AnimatorSet mAnimSet, float start, float end) {
animationControls.add(new AnimationControl(mImageView, mAnimSet, start, end));
}
public void addAnimatorSet(Object target, AnimatorSet mAnimSet, float start, float end) {
animationControls.add(new AnimationControl(target, mAnimSet, start, end));
}
public void start() {
ValueAnimator colorAnim = ObjectAnimator.ofInt(new Object() {
private int dummy;
public int getDummy() {
return dummy;
}
public void setDummy(int dummy) {
this.dummy = dummy;
}
}, "dummy", 0, 1);
colorAnim.setDuration(Integer.MAX_VALUE);
mInternalAnimSet.play(colorAnim).with(mTimeAnim);
mInternalAnimSet.start();
if (mMenuItem != null) {
mMenuItem.setActionView(mImageView);
}
for (AnimationControl ctrl : animationControls) {
ctrl.start();
}
}
public void end() {
mTimeAnim.end();
if (mMenuItem != null) {
mMenuItem.setActionView(null);
}
}
public void updateProgress(float progress) {
for (AnimationControl ctrl : animationControls) {
ctrl.updateProgress(progress);
}
}
#Override
public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
for (AnimationControl ctrl : animationControls) {
ctrl.updateState();
}
}
}
AnimationControl.java: Controls the progression of transition.
public class AnimationControl {
private AnimatorSet mAnimSet;
private Object target;
private float start;
private float end = 1.0f;
private float progressWidth;
private long time;
private boolean started;
private long mStartDelay;
private long mDuration;
private long mTotalDuration;
public AnimationControl(AnimatorSet mAnimSet, float start, float end) {
this(null, mAnimSet, start, end);
}
public AnimationControl(Object target, AnimatorSet mAnimSet, float start, float end) {
for (Animator animator : mAnimSet.getChildAnimations()) {
if (!(animator instanceof ValueAnimator)) {
throw new UnsupportedOperationException("Only ValueAnimator and its subclasses are supported");
}
}
this.target = target;
this.mAnimSet = mAnimSet;
mStartDelay = mAnimSet.getStartDelay();
mDuration = mAnimSet.getDuration();
if (mAnimSet.getDuration() >= 0) {
long duration = mAnimSet.getDuration();
for (Animator animator : mAnimSet.getChildAnimations()) {
animator.setDuration(duration);
}
} else {
for (Animator animator : mAnimSet.getChildAnimations()) {
long endTime = animator.getStartDelay() + animator.getDuration();
if (mDuration < endTime) {
mDuration = endTime;
}
}
}
mTotalDuration = mStartDelay + mDuration;
this.start = start;
this.end = end;
progressWidth = Math.abs(end - start);
}
public void start() {
if (target != null) {
for (Animator animator : mAnimSet.getChildAnimations()) {
animator.setTarget(target);
}
}
}
public void updateProgress(float progress) {
if (start < end && progress >= start && progress <= end || start > end && progress >= end && progress <= start) {
if (start < end) {
time = (long) (mTotalDuration * (progress - start) / progressWidth);
} else {
time = (long) (mTotalDuration - mTotalDuration * (progress - end) / progressWidth);
}
time -= mStartDelay;
if (time > 0) {
started = true;
}
Log.e(getClass().getSimpleName(), "updateState: " + mTotalDuration + ";" + time+"/"+start+"/"+end);
} else {
//forward
if (start < end) {
if (progress < start) {
time = 0;
} else if (progress > end) {
time = mTotalDuration;
}
//backward
} else if (start > end) {
if (progress > start) {
time = 0;
} else if (progress > end) {
time = mTotalDuration;
}
}
started = false;
}
}
public void updateState() {
if (started) {
for (Animator animator : mAnimSet.getChildAnimations()) {
ValueAnimator va = (ValueAnimator) animator;
long absTime = time - va.getStartDelay();
if (absTime > 0) {
va.setCurrentPlayTime(absTime);
}
}
}
}
}
ProgressDrawerListener.java: This listens for DrawerLayout state update and setup the required animation.
public class ProgressDrawerListener implements DrawerLayout.DrawerListener {
private final List<ProgressAnimator> mAnimatingMenuItems = new ArrayList<>();
private final Toolbar mToolbar;
private final ActionBarDrawerToggle mDrawerToggle;
private DrawerLayout.DrawerListener mDrawerListener;
private MenuItemAnimation mMenuItemAnimation;
private Animation mAnimation;
private boolean started;
private boolean mOpened;
private Activity mActivity;
private boolean mInvalidateOptionOnOpenClose;
public ProgressDrawerListener(Toolbar mToolbar, ActionBarDrawerToggle mDrawerToggle) {
this.mToolbar = mToolbar;
this.mDrawerToggle = mDrawerToggle;
}
#Override
public void onDrawerOpened(View view) {
mDrawerToggle.onDrawerOpened(view);
clearAnimation();
started = false;
if (mDrawerListener != null) {
mDrawerListener.onDrawerOpened(view);
}
mToolbar.getMenu().setGroupVisible(0, false); //TODO not always needed
mOpened=true;
mActivity.invalidateOptionsMenu();
}
#Override
public void onDrawerClosed(View view) {
mDrawerToggle.onDrawerClosed(view);
clearAnimation();
started = false;
if (mDrawerListener != null) {
mDrawerListener.onDrawerClosed(view);
}
mOpened=false;
mActivity.invalidateOptionsMenu();
}
#Override
public void onDrawerStateChanged(int state) {
mDrawerToggle.onDrawerStateChanged(state);
switch (state) {
case DrawerLayout.STATE_DRAGGING:
case DrawerLayout.STATE_SETTLING:
if (mAnimatingMenuItems.size() > 0 || started) {
break;
}
started = true;
setupAnimation();
break;
case DrawerLayout.STATE_IDLE:
clearAnimation();
started = false;
break;
}
if (mDrawerListener != null) {
mDrawerListener.onDrawerStateChanged(state);
}
}
private void setupAnimation() {
mToolbar.getMenu().setGroupVisible(0, true); //TODO not always needed
mAnimatingMenuItems.clear();
for (int i = 0; i < mToolbar.getChildCount(); i++) {
final View v = mToolbar.getChildAt(i);
if (v instanceof ActionMenuView) {
int menuItemCount = 0;
int childCount = ((ActionMenuView) v).getChildCount();
for (int j = 0; j < childCount; j++) {
if (((ActionMenuView) v).getChildAt(j) instanceof ActionMenuItemView) {
menuItemCount++;
}
}
for (int j = 0; j < childCount; j++) {
final View innerView = ((ActionMenuView) v).getChildAt(j);
if (innerView instanceof ActionMenuItemView) {
MenuItem mMenuItem = ((ActionMenuItemView) innerView).getItemData();
ProgressAnimator offsetAnimator = new ProgressAnimator(mToolbar.getContext(), mMenuItem);
if(mMenuItemAnimation!=null) {
mMenuItemAnimation.setupAnimation(mMenuItem, offsetAnimator, j, menuItemCount);
}
if(mAnimation!=null) {
mAnimation.setupAnimation(offsetAnimator);
}
offsetAnimator.start();
mAnimatingMenuItems.add(offsetAnimator);
}
}
}
}
onDrawerSlide(null, mOpened ? 1f : 0f);
Log.e(getClass().getSimpleName(), "setupAnimation: "+mAnimatingMenuItems.size()); //TODO
}
#Override
public void onDrawerSlide(View view, float slideOffset) {
for (ProgressAnimator ani : mAnimatingMenuItems) {
ani.updateProgress(slideOffset);
}
if(view==null) {
return;
}
mDrawerToggle.onDrawerSlide(view, slideOffset);
if (mDrawerListener != null) {
mDrawerListener.onDrawerSlide(view, slideOffset);
}
}
private void clearAnimation() {
for (ProgressAnimator ani : mAnimatingMenuItems) {
ani.end();
}
mAnimatingMenuItems.clear();
}
public void setDrawerListener(DrawerLayout.DrawerListener mDrawerListener) {
this.mDrawerListener = mDrawerListener;
}
public MenuItemAnimation getMenuItemAnimation() {
return mMenuItemAnimation;
}
public void setMenuItemAnimation(MenuItemAnimation mMenuItemAnimation) {
this.mMenuItemAnimation = mMenuItemAnimation;
}
public Animation getAnimation() {
return mAnimation;
}
public void setAnimation(Animation mAnimation) {
this.mAnimation = mAnimation;
}
public void setmInvalidateOptionOnOpenClose(Activity activity, boolean invalidateOptionOnOpenClose) {
mActivity=activity;
mInvalidateOptionOnOpenClose = invalidateOptionOnOpenClose;
}
public interface MenuItemAnimation {
public void setupAnimation(MenuItem mMenuItem, ProgressAnimator offsetAnimator, int itemIndex, int menuCount);
}
public interface Animation {
public void setupAnimation(ProgressAnimator offsetAnimator);
}
}
Set up in Activity: The example code below switches between two different menu options between opened and closed state. Optionally add offsetDrawerListener.setDrawerListener(DrawerListener) if you need to have your own DrawerListener.:
#Override
protected void onCreate(Bundle savedInstanceState) {
//other init
mProgressDrawerListener =new ProgressDrawerListener(toolbar, mDrawerToggle);
mProgressDrawerListener.setmInvalidateOptionOnOpenClose(this, true);
mOpenAnimation = new ProgressDrawerListener.MenuItemAnimation() {
#Override
public void setupAnimation(MenuItem mMenuItem, ProgressAnimator offsetAnimator, int itemIndex, int menuCount) {
MainActivity.this.setupAnimation(true, offsetAnimator, itemIndex);
}
};
mCloseAnimation = new ProgressDrawerListener.MenuItemAnimation() {
#Override
public void setupAnimation(MenuItem mMenuItem, ProgressAnimator offsetAnimator, int itemIndex, int menuCount) {
MainActivity.this.setupAnimation(false, offsetAnimator, itemIndex);
}
};
mDrawerLayout.setDrawerListener(mProgressDrawerListener);
}
//customize your animation here
private void setupAnimation(boolean open, ProgressAnimator offsetAnimator, int itemIndex) {
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(null, "alpha", 1.0f, 0f),
ObjectAnimator.ofFloat(null, "scaleX", 1.0f, 0f)
);
set.setStartDelay(itemIndex * 200);
set.setDuration(1000 - itemIndex * 200); //not the actual time the animation will be played
if(open) {
offsetAnimator.addAnimatorSet(set, 0, 1);
} else {
offsetAnimator.addAnimatorSet(set, 1, 0);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Only show items in the action bar relevant to this screen
// if the drawer is not showing. Otherwise, let the drawer
// decide what to show in the action bar.
if(mDrawerLayout.isDrawerOpen(findViewById(R.id.drawerList))) {
getMenuInflater().inflate(R.menu.drawer, menu);
mProgressDrawerListener.setMenuItemAnimation(
mCloseAnimation);
} else {
getMenuInflater().inflate(R.menu.main, menu);
mProgressDrawerListener.setMenuItemAnimation(
mOpenAnimation);
mDrawerLayout.setDrawerListener(mProgressDrawerListener);
}
return super.onCreateOptionsMenu(menu);
}
menu_animation.xml: This is to get the custom ActionView to have the same layout as the view used by MenuIem
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/menu_animation"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minWidth="#*android:dimen/action_button_min_width"
android:padding="8dp"
style="#style/Widget.AppCompat.ActionBar" />
I think I finally found your answer. This was way harder to find than I thought. If you take a look at this link: http://suhan.in/material-design-toolbar-animation/
The first one explains how this is done.
Below you find my own code snippet of how it could be done with only the menu items:
for(int i = 0; i < toolbarView.getChildCount(); i++)
{
final View v = toolbarView.getChildAt(i);
if(v instanceof ActionMenuView)
{
for(int j = 0; j < ((ActionMenuView)v).getChildCount(); j++)
{
final View innerView = ((ActionMenuView)v).getChildAt(j);
if(innerView instanceof ActionMenuItemView)
{
innerView.setTranslationY(-30);
innerView.animate().setStartDelay(100 + (j * 10)).setDuration(200).translationY(0);
}
}
}
}
This is the animation for the Y-axis. You can also add the animation for size, which I think they do in the design guidelines. Also if you don't want them to start simultaniously, you can set startDelay and add extra like this: setStartDelay(i * 10). This way each item starts the animation a little later. I already put this in the code snippet, but tweak it as how you would like it.

Unable to hide IndexScroll from listview

I have a custom listview which is having a custom inexscroller which draws itself when you scroll through the list. Now that doesnt look nice when my listview is not having too many items. So what I want to do is to hide the IndexScroller when the items are less than a particular number to be scrollable. I have done everything but I am not able to hide the list view. Please help:
Here are the classes used:
IndexableListViewActivity
public class IndexableListViewActivity extends Activity implements OnClickListener
{
private ArrayList<String> mItems;
private IndexableListView mListView;
TextView MyTasks, TeamTasks, username, fullusername;
RelativeLayout listlay;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mItems = new ArrayList<String>();
mItems.add("Diary of a Wimpy Kid 6: Cabin Fever");
mItems.add("Steve Jobs");
mItems.add("Inheritance (The Inheritance Cycle)");
mItems.add("11/22/63: A Novel");
mItems.add("The Hunger Games");
mItems.add("The LEGO Ideas Book");
mItems.add("Explosive Eighteen: A Stephanie Plum Novel");
mItems.add("Catching Fire (The Second Book of the Hunger Games)");
mItems.add("Elder Scrolls V: Skyrim: Prima Official Game Guide");
mItems.add("Death Comes to Pemberley");
mItems.add("Diary of a Wimpy Kid 6: Cabin Fever");
mItems.add("Steve Jobs");
mItems.add("Inheritance (The Inheritance Cycle)");
mItems.add("11/22/63: A Novel");
mItems.add("The Hunger Games");
mItems.add("The LEGO Ideas Book");
mItems.add("Explosive Eighteen: A Stephanie Plum Novel");
mItems.add("Catching Fire (The Second Book of the Hunger Games)");
mItems.add("Elder Scrolls V: Skyrim: Prima Official Game Guide");
mItems.add("Death Comes to Pemberley");
mItems.add("Make this list longer");
mItems.add("A");
mItems.add("B");
// mItems.add("C");
// mItems.add("D");
// mItems.add("E");
// mItems.add("F");
// mItems.add("H");
// mItems.add("I");
// mItems.add("J");
// mItems.add("K");
// mItems.add("L");
// mItems.add("M");
// mItems.add("N");
// mItems.add("O");
// mItems.add("P");
// mItems.add("Q");
// mItems.add("R");
// mItems.add("S");
// mItems.add("T");
// mItems.add("U");
// mItems.add("V");
// mItems.add("W");
// mItems.add("X");
// mItems.add("Y");
// mItems.add("Z");
Collections.sort(mItems);
ContentAdapter adapter = new ContentAdapter(this,
android.R.layout.simple_list_item_1, mItems);
mListView = (IndexableListView) findViewById(R.id.listview);
mListView.setAdapter(adapter);
mListView.setFastScrollEnabled(true);
MyTasks = (TextView)findViewById(R.id.myTasks);
MyTasks.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
MyTasks.setBackgroundResource(R.drawable.rectangle_selected);
TeamTasks.setBackgroundResource(R.drawable.rectangle);
if(mListView.getLastVisiblePosition() + 1 == mListView.getCount()) {
Toast.makeText(getBaseContext(), "No need to scroll", Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(getBaseContext(), "Need to scroll", Toast.LENGTH_SHORT).show();
}
}
});
TeamTasks = (TextView)findViewById(R.id.teamTasks);
TeamTasks.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
TeamTasks.setBackgroundResource(R.drawable.rectangle_selected);
MyTasks.setBackgroundResource(R.drawable.rectangle);
}
});
}
private class ContentAdapter extends ArrayAdapter<String> implements SectionIndexer {
private String mSections = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public ContentAdapter(Context context, int textViewResourceId,
List<String> objects) {
super(context, textViewResourceId, objects);
}
#Override
public int getPositionForSection(int section) {
// If there is no item for current section, previous section will be selected
for (int i = section; i >= 0; i--) {
for (int j = 0; j < getCount(); j++) {
if (i == 0) {
// For numeric section
for (int k = 0; k <= 9; k++) {
if (StringMatcher.match(String.valueOf(getItem(j).charAt(0)), String.valueOf(k)))
return j;
}
} else {
if (StringMatcher.match(String.valueOf(getItem(j).charAt(0)), String.valueOf(mSections.charAt(i))))
return j;
}
}
}
return 0;
}
#Override
public int getSectionForPosition(int position) {
return 0;
}
#Override
public Object[] getSections() {
String[] sections = new String[mSections.length()];
for (int i = 0; i < mSections.length(); i++)
sections[i] = String.valueOf(mSections.charAt(i));
return sections;
}
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
StrngMatcher.java
public class StringMatcher {
public static boolean match(String value, String keyword) {
if (value == null || keyword == null)
return false;
if (keyword.length() > value.length())
return false;
int i = 0, j = 0;
do {
int vi = value.charAt(i);
int kj = keyword.charAt(j);
if (isKorean(vi) && isInitialSound(kj)) {
} else {
if (vi == kj) {
i++;
j++;
} else if (j > 0)
break;
else
i++;
}
} while (i < value.length() && j < keyword.length());
return (j == keyword.length())? true : false;
}
private static boolean isKorean(int i) {
return false;
}
private static boolean isInitialSound(int i) {
return false;
}
}
IndexableListView.java
public class IndexableListView extends ListView {
private boolean mIsFastScrollEnabled = false;
private IndexScroller mScroller = null;
private GestureDetector mGestureDetector = null;
public IndexableListView(Context context) {
super(context);
}
public IndexableListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public IndexableListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean isFastScrollEnabled() {
return mIsFastScrollEnabled;
}
#Override
public void setFastScrollEnabled(boolean enabled) {
mIsFastScrollEnabled = enabled;
if (mIsFastScrollEnabled) {
if (mScroller == null)
mScroller = new IndexScroller(getContext(), this);
}
else {
if (mScroller != null) {
mScroller.hide();
mScroller = null;
}
}
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
// Overlay index bar
if (mScroller != null)
mScroller.draw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Intercept ListView's touch event
if (mScroller != null && mScroller.onTouchEvent(ev))
return true;
if (mGestureDetector == null) {
mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY) {
// If fling happens, index bar shows
if(mScroller!=null)
mScroller.show();
return super.onFling(e1, e2, velocityX, velocityY);
}
});
}
mGestureDetector.onTouchEvent(ev);
return super.onTouchEvent(ev);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
#Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
if (mScroller != null)
mScroller.setAdapter(adapter);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (mScroller != null)
mScroller.onSizeChanged(w, h, oldw, oldh);
}
}
IndexScroller.java
public class IndexScroller {
private float mIndexbarWidth;
private float mIndexbarMargin;
private float mPreviewPadding;
private float mDensity;
private float mScaledDensity;
private float mAlphaRate;
private int mState = STATE_HIDDEN;
private int mListViewWidth;
private int mListViewHeight;
private int mCurrentSection = -1;
private boolean mIsIndexing = false;
private ListView mListView = null;
private SectionIndexer mIndexer = null;
private String[] mSections = null;
private RectF mIndexbarRect;
private static final int STATE_HIDDEN = 0;
private static final int STATE_SHOWING = 1;
private static final int STATE_SHOWN = 2;
private static final int STATE_HIDING = 3;
public IndexScroller(Context context, ListView lv) {
mDensity = context.getResources().getDisplayMetrics().density;
mScaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
mListView = lv;
setAdapter(mListView.getAdapter());
mIndexbarWidth = 20 * mDensity;
mIndexbarMargin = 2 * mDensity;
mPreviewPadding = 5 * mDensity;
}
public void draw(Canvas canvas) {
if (mState == STATE_HIDDEN)
return;
// mAlphaRate determines the rate of opacity
Paint indexbarPaint = new Paint();
indexbarPaint.setColor(Color.BLACK);
indexbarPaint.setAlpha((int) (64 * mAlphaRate));
indexbarPaint.setAntiAlias(true);
canvas.drawRoundRect(mIndexbarRect, 5 * mDensity, 5 * mDensity, indexbarPaint);
if (mSections != null && mSections.length > 0) {
// Preview is shown when mCurrentSection is set
if (mCurrentSection >= 0) {
Paint previewPaint = new Paint();
previewPaint.setColor(Color.BLACK);
previewPaint.setAlpha(96);
previewPaint.setAntiAlias(true);
previewPaint.setShadowLayer(3, 0, 0, Color.argb(64, 0, 0, 0));
Paint previewTextPaint = new Paint();
previewTextPaint.setColor(Color.WHITE);
previewTextPaint.setAntiAlias(true);
previewTextPaint.setTextSize(50 * mScaledDensity);
float previewTextWidth = previewTextPaint.measureText(mSections[mCurrentSection]);
float previewSize = 2 * mPreviewPadding + previewTextPaint.descent() - previewTextPaint.ascent();
RectF previewRect = new RectF((mListViewWidth - previewSize) / 2
, (mListViewHeight - previewSize) / 2
, (mListViewWidth - previewSize) / 2 + previewSize
, (mListViewHeight - previewSize) / 2 + previewSize);
canvas.drawRoundRect(previewRect, 5 * mDensity, 5 * mDensity, previewPaint);
canvas.drawText(mSections[mCurrentSection], previewRect.left + (previewSize - previewTextWidth) / 2 - 1
, previewRect.top + mPreviewPadding - previewTextPaint.ascent() + 1, previewTextPaint);
}
Paint indexPaint = new Paint();
indexPaint.setColor(Color.WHITE);
indexPaint.setAlpha((int) (255 * mAlphaRate));
indexPaint.setAntiAlias(true);
indexPaint.setTextSize(12 * mScaledDensity);
float sectionHeight = (mIndexbarRect.height() - 2 * mIndexbarMargin) / mSections.length;
float paddingTop = (sectionHeight - (indexPaint.descent() - indexPaint.ascent())) / 2;
for (int i = 0; i < mSections.length; i++) {
float paddingLeft = (mIndexbarWidth - indexPaint.measureText(mSections[i])) / 2;
canvas.drawText(mSections[i], mIndexbarRect.left + paddingLeft
, mIndexbarRect.top + mIndexbarMargin + sectionHeight * i + paddingTop - indexPaint.ascent(), indexPaint);
}
}
}
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// If down event occurs inside index bar region, start indexing
if (mState != STATE_HIDDEN && contains(ev.getX(), ev.getY())) {
setState(STATE_SHOWN);
// It demonstrates that the motion event started from index bar
mIsIndexing = true;
// Determine which section the point is in, and move the list to that section
mCurrentSection = getSectionByPoint(ev.getY());
mListView.setSelection(mIndexer.getPositionForSection(mCurrentSection));
return true;
}
break;
case MotionEvent.ACTION_MOVE:
if (mIsIndexing) {
// If this event moves inside index bar
if (contains(ev.getX(), ev.getY())) {
// Determine which section the point is in, and move the list to that section
mCurrentSection = getSectionByPoint(ev.getY());
mListView.setSelection(mIndexer.getPositionForSection(mCurrentSection));
}
return true;
}
break;
case MotionEvent.ACTION_UP:
if (mIsIndexing) {
mIsIndexing = false;
mCurrentSection = -1;
}
if (mState == STATE_SHOWN)
setState(STATE_HIDING);
break;
}
return false;
}
public void onSizeChanged(int w, int h, int oldw, int oldh) {
mListViewWidth = w;
mListViewHeight = h;
mIndexbarRect = new RectF(w - mIndexbarMargin - mIndexbarWidth
, mIndexbarMargin
, w - mIndexbarMargin
, h - mIndexbarMargin);
}
public void show() {
if (mState == STATE_HIDDEN)
setState(STATE_SHOWING);
else if (mState == STATE_HIDING)
setState(STATE_HIDING);
}
public void hide() {
if (mState == STATE_SHOWN)
setState(STATE_HIDING);
}
public void setAdapter(Adapter adapter) {
if (adapter instanceof SectionIndexer) {
mIndexer = (SectionIndexer) adapter;
mSections = (String[]) mIndexer.getSections();
}
}
private void setState(int state) {
if (state < STATE_HIDDEN || state > STATE_HIDING)
return;
mState = state;
switch (mState) {
case STATE_HIDDEN:
// Cancel any fade effect
mHandler.removeMessages(0);
break;
case STATE_SHOWING:
// Start to fade in
mAlphaRate = 0;
fade(0);
break;
case STATE_SHOWN:
// Cancel any fade effect
mHandler.removeMessages(0);
break;
case STATE_HIDING:
// Start to fade out after three seconds
mAlphaRate = 1;
fade(3000);
break;
}
}
private boolean contains(float x, float y) {
// Determine if the point is in index bar region, which includes the right margin of the bar
return (x >= mIndexbarRect.left && y >= mIndexbarRect.top && y <= mIndexbarRect.top + mIndexbarRect.height());
}
private int getSectionByPoint(float y) {
if (mSections == null || mSections.length == 0)
return 0;
if (y < mIndexbarRect.top + mIndexbarMargin)
return 0;
if (y >= mIndexbarRect.top + mIndexbarRect.height() - mIndexbarMargin)
return mSections.length - 1;
return (int) ((y - mIndexbarRect.top - mIndexbarMargin) / ((mIndexbarRect.height() - 2 * mIndexbarMargin) / mSections.length));
}
private void fade(long delay) {
mHandler.removeMessages(0);
mHandler.sendEmptyMessageAtTime(0, SystemClock.uptimeMillis() + delay);
}
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (mState) {
case STATE_SHOWING:
// Fade in effect
mAlphaRate += (1 - mAlphaRate) * 0.2;
if (mAlphaRate > 0.9) {
mAlphaRate = 1;
setState(STATE_SHOWN);
}
mListView.invalidate();
fade(10);
break;
case STATE_SHOWN:
// If no action, hide automatically
setState(STATE_HIDING);
break;
case STATE_HIDING:
// Fade out effect
mAlphaRate -= mAlphaRate * 0.2;
if (mAlphaRate < 0.1) {
mAlphaRate = 0;
setState(STATE_HIDDEN);
}
mListView.invalidate();
fade(10);
break;
}
}
};
}
In this, IndexableListView and IndexScroller are in same package and other 2 classes are in 2 different packages.
Please help how to just hide the IndexScroller so that it doesnt show up on touches.
Since no one bothered to answer, here is the thing to be done.,
For all lines where state is set as shown in IndexScroller.java, enclose them in:
isTrue = mListView.getLastVisiblePosition() + 1 == mListView.getCount();
if(!isTrue)
{
//State Shown
}

Categories