In my scenario if I click on a expanded card from card_1 then all other cards should be invisible. Also, can I change the positions of the card runtime so it can move 2nd card on the position of the first and it will expand to full screen so there will be no need to hide other cards.
Please check the attached image.
public class DashboardMarketAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int VIEW_TYPE_TOP_MENU = 0;
private static final int VIEW_TYPE_MARKET_MOVERS = 1;
private static final int VIEW_TYPE_DERIVATIVES = 2;
private static final int VIEW_TYPE_NEWS = 3;
private final Context context;
private final String[] menuItems;
public DashboardMarketAdapter(Context context, String[] menuItems) {
super();
this.context = context;
this.menuItems = menuItems;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
switch (viewType) {
case VIEW_TYPE_TOP_MENU:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_top_view, parent, false);
return new TopMenuViewHolder(v, parent.getContext());
case VIEW_TYPE_MARKET_MOVERS:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_market_movers, parent, false);
return new MarketMoversViewHolder(v, parent.getContext());
case VIEW_TYPE_DERIVATIVES:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_market_derivatives, parent, false);
return new MarketDerivativesViewHolder(v);
case VIEW_TYPE_NEWS:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_top_news, parent, false);
return new TopNewsViewHolder(v);
default:
throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
}
/**
* The length of the string array will tell recycler view the count of the card views.
*/
#Override
public int getItemCount() {
return menuItems.length;
}
/**
* #param position this will return the view of the card according to the position of the card.
*/
#Override
public int getItemViewType(int position) {
switch (position) {
case VIEW_TYPE_TOP_MENU:
return VIEW_TYPE_TOP_MENU;
case VIEW_TYPE_MARKET_MOVERS:
return VIEW_TYPE_MARKET_MOVERS;
case VIEW_TYPE_DERIVATIVES:
return VIEW_TYPE_DERIVATIVES;
case VIEW_TYPE_NEWS:
return VIEW_TYPE_NEWS;
default:
return 0;
}
}
/**
* This view holder is holding the view of topmost card,
* which contains tab and viewpager.
*/
static class TopMenuViewHolder extends RecyclerView.ViewHolder {
#InjectView(R.id.pager)
ViewPager top_view_pager;
#InjectView(R.id.tabs)
DashboardSlidingTabLayout top_view_tabs;
#InjectView(R.id.expand)
Button btnExpand;
#InjectView(R.id.card_view)
CardView cardView;
private DashboardTabAdapter adapter;
private CharSequence Titles[];
private int NumberOfTabs;
int pos;
static int setView = 0;
int minHeight;
public static final int TOP_VIEW = 0;
public static final int INDICES = 1;
public static final int COMMODITY = 2;
public static final int CURRENCY = 3;
public TopMenuViewHolder(View itemView, final Context context) {
super(itemView);
ButterKnife.inject(this, itemView);
Titles = context.getResources().getStringArray(R.array.string_array_dashboard_tab_menu);
Bundle bundle = new Bundle();
bundle.putInt("setView", setView);
NumberOfTabs = Titles.length;
adapter = new DashboardTabAdapter(((FragmentActivity) context).getSupportFragmentManager(), Titles, NumberOfTabs, bundle);
top_view_pager.setAdapter(adapter);
top_view_tabs.setViewPager(top_view_pager);
/**
* Here visibility of Expand button is decided upon the tab selected.
*/
top_view_pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
pos = position;
if (position > 0) {
btnExpand.setVisibility(View.VISIBLE);
initializeTab();
} else if (position == 0) {
btnExpand.setVisibility(View.GONE);
collapseView();
}
}
});
/**
* This will be used to give initial height for the top card.
*/
cardView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
cardView.getViewTreeObserver().removeOnPreDrawListener(this);
minHeight = cardView.getHeight();
ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
layoutParams.height = minHeight;
cardView.setLayoutParams(layoutParams);
return true;
}
});
/**
* Here you can get the height of the screen
*/
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dimension = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dimension);
final int height = dimension.heightPixels;
/**
* Here you can add logic for expanding a card.
*/
btnExpand.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.i("Dashboard", "Current Fragment" + top_view_pager.getCurrentItem());
new ChildRecyclerView(context, true);
new ParentRecyclerView(context, false);
toggleCardViewnHeight(height);
}
});
}
/**
* This method will toggle expand and collapse of card view.
*/
private void toggleCardViewnHeight(int height) {
int ViewMinHeight = minHeight;
if (cardView.getHeight() == ViewMinHeight) {
// expand
expandView(height);
} else {
// collapse
collapseView();
}
}
/**
* This method will initialize tab adapter depending upon the page selected.
*/
public void initializeTab() {
switch (top_view_pager.getCurrentItem()) {
case INDICES:
((IndicesFragment) adapter.getRegisteredFragment(INDICES)).initUI();
break;
case COMMODITY:
((CommodityDashboardFragment) adapter.getRegisteredFragment(COMMODITY)).initUI();
break;
case CURRENCY:
((CurrencyDashboardFragment) adapter.getRegisteredFragment(CURRENCY)).initUI();
break;
}
}
/**
* Collapse View Animation
*/
public void collapseView() {
BaseFragment.isExpanded = 0;
ValueAnimator anim = ValueAnimator.ofInt(cardView.getMeasuredHeightAndState(),
minHeight);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
layoutParams.height = val;
cardView.setLayoutParams(layoutParams);
}
});
anim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
initializeTab();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
anim.start();
}
/**
* Expand View Animation
*/
public void expandView(int height) {
BaseFragment.isExpanded = 1;
ValueAnimator anim = ValueAnimator.ofInt(cardView.getMeasuredHeightAndState(),
height);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
layoutParams.height = val;
cardView.setLayoutParams(layoutParams);
}
});
anim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
initializeTab();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
anim.start();
}
}
/**
* This view holder is holding the view of MarketMovers card,
*/
static class MarketMoversViewHolder extends RecyclerView.ViewHolder {
#InjectView(R.id.btnExpand)
Button btnExpand;
#InjectView(R.id.card_view)
CardView cardView;
#InjectView(R.id.relativeLayout)
RelativeLayout relativeLayout;
int height;
String []menuItem={"MARKET MOVERS","DERIVATIVES","TOP NEWS"};
public MarketMoversViewHolder(View itemView, final Context context) {
super(itemView);
ButterKnife.inject(this, itemView);
relativeLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
relativeLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
height = relativeLayout.getHeight();
ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
layoutParams.height = 400;
cardView.setLayoutParams(layoutParams);
}
});
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dimension = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dimension);
int width = dimension.widthPixels;
final int screenHeight = dimension.heightPixels;
final int fullHeight = screenHeight - 400;
btnExpand.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
toggleCardViewnHeight(fullHeight);
DashboardMarketAdapter DM =new DashboardMarketAdapter(context,menuItem);
DM.notifyDataSetChanged();
}
});
}
private void toggleCardViewnHeight(int height) {
int ViewMinHeight = 400;
if (cardView.getHeight() == ViewMinHeight) {
// expand
ValueAnimator anim = ValueAnimator.ofInt(cardView.getMeasuredHeightAndState(),
height);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
layoutParams.height = val;
cardView.setLayoutParams(layoutParams);
}
});
anim.start();
} else {
// collapse
ValueAnimator anim = ValueAnimator.ofInt(cardView.getMeasuredHeightAndState(),
ViewMinHeight);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
layoutParams.height = val;
cardView.setLayoutParams(layoutParams);
}
});
anim.start();
}
}
}
/**
* This view holder is holding the view of Derivatives card,
*/
private class MarketDerivativesViewHolder extends RecyclerView.ViewHolder {
public MarketDerivativesViewHolder(View v) {
super(v);
}
}
/**
* This view holder is holding the view of NEWS card,
*/
private class TopNewsViewHolder extends RecyclerView.ViewHolder {
public TopNewsViewHolder(View v) {
super(v);
}
}
}
Related
When expanding and collapsing my Recyclerview items, divider lines drawn multiple times or overdrawn with items.
Also when expanding and collapsing views, dividers thickness is getting reduced.
My problem is divider line drawn each and every time I expand and collapse item in Recyclerview.
So is it possible to prevent divider line drawing if it already drawn?
While expanding an item, divider line will move according to the view.?
Below is my RecyclerView Decoration Class used for divider line,
public class SeparatorDecoration extends RecyclerView.ItemDecoration {
private final Paint mPaint;
/**
* Create a decoration that draws a line in the given color and width between the items in the view.
* #param context a context to access the resources.
* #param color the color of the separator to draw.
* #param heightDp the height of the separator in dp.
*/
public SeparatorDecoration(#NonNull Context context, #ColorInt int color,
#FloatRange(from = 0, fromInclusive = false) float heightDp) {
mPaint = new Paint();
mPaint.setColor(color);
final float thickness = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
heightDp, context.getResources().getDisplayMetrics());
mPaint.setStrokeWidth(thickness);
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// we want to retrieve the position in the list
final int position = params.getViewAdapterPosition();
// and add a separator to any view but the last one
if (position <state.getItemCount()) {
outRect.set(40, 0, 40, (int) mPaint.getStrokeWidth()); // left, top, right, bottom
} else {
outRect.setEmpty(); // 0, 0, 0, 0
}
}
#Override
public void onDrawOver(#NonNull Canvas c, #NonNull RecyclerView parent, #NonNull RecyclerView.State state) {
final int offset = (int) (mPaint.getStrokeWidth() / 2);
// this will iterate over every visible view
for (int i = 0; i < parent.getChildCount(); i++) {
// get the view
final View view = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// get the position
final int position = params.getViewAdapterPosition();
// and finally draw the separator
if (position < parent.getChildCount()) {
final int ty = (int)(view.getTranslationY() + 0.5f);
final int top = view.getBottom() - params.bottomMargin + ty;
final int bottom = top + (int) mPaint.getStrokeWidth();
c.drawLine(view.getLeft(), view.getBottom() + offset, view.getRight(), view.getBottom() + offset, mPaint);
}
}
}
}
below is my RecyclerView Adapter class,
public class DisplayNotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private Context context;
private List<NotificationDetails> notificationRecords;
private DeleteNotificationListener deleteNotificationListener;
private String TAG = DisplayNotificationAdapter.class.getSimpleName();
interface DeleteNotificationListener {
void updateNotificationList(List<NotificationDetails> details);
}
public DisplayNotificationAdapter(Context context, DeleteNotificationListener listener, List < NotificationDetails > notificationRecordsList) {
this.context = context;
this.deleteNotificationListener = listener;
this.notificationRecords = notificationRecordsList;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder (#NonNull ViewGroup parent, int viewType){
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
AndroidLogger.log(5,TAG,"oncreate");
View listItem = layoutInflater.inflate(R.layout.display_notification_recycler_view_list_item, parent, false);
return new NotificationViewHolder(listItem);
}
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
public void onBindViewHolder (#NonNull RecyclerView.ViewHolder holder,int position){
NotificationDetails notification = notificationRecords.get(position);
NotificationViewHolder viewHolder = (NotificationViewHolder) holder;
String currentDateString = DateFormat.getDateInstance().format(Long.parseLong(notification.getTimeStamp()));
String filePath=generateFilePath(notification.getFileName());
Bitmap myBitmap = BitmapFactory.decodeFile(filePath);
#SuppressLint("SimpleDateFormat")
DateFormat dateFormat = new SimpleDateFormat("hh:mm aa");
String time = dateFormat.format(Long.parseLong(notification.getTimeStamp()));
if (notification.isExpanded()) {
viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Up Arrow")));
expandView(viewHolder.notificationImageview);
}
else {
viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Down Arrow")));
collapseView(viewHolder.notificationImageview);
}
viewHolder.notificationImageview.setImageBitmap(myBitmap);
viewHolder.notificationTextView.setText(notification.getMessage());
viewHolder.notificationTimeTextView.setText(time);
Calendar now = Calendar.getInstance();
Calendar date = Calendar.getInstance();
date.setTimeInMillis(Long.parseLong(notification.getTimeStamp()));
viewHolder.expandCollapseImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(notification.isExpanded()) {
notification.setExpanded(false);
viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Down Arrow")));
viewHolder.notificationTextView.setMaxLines(1);
notifyItemChanged(position);
}
else {
notification.setExpanded(true);
viewHolder.expandCollapseImageView.setImageDrawable(context.getDrawable(ImageDrawable.getDrawable("Up Arrow")));
viewHolder.notificationTextView.setMaxLines(Integer.MAX_VALUE);
notifyItemChanged(position);
}
}
});
if (now.get(Calendar.DATE) == date.get(Calendar.DATE))
viewHolder.notificationDateTextView.setText("Today");
else if (now.get(Calendar.DATE) - date.get(Calendar.DATE) == 1)
viewHolder.notificationDateTextView.setText("Yesterday");
else
viewHolder.notificationDateTextView.setText(currentDateString);
if(notification.getTitle()==null)
viewHolder.notificationTitleTextView.setText("title");
else
viewHolder.notificationTitleTextView.setText(notification.getTitle());
}
private String generateFilePath(String fileName) {
File imageFileDirectory = context.getDir("image", Context.MODE_PRIVATE); //Creating an internal dir;
if (!imageFileDirectory.exists()) {
imageFileDirectory.mkdirs();
}
/*
* app server provide "U" file name after we set read status they provide same file name as "R"
*/
String createFilePath = imageFileDirectory + "/" + fileName;
return createFilePath;
}
public void removeSingleNotification ( int position){
DatabaseHelper databaseHelper = new DatabaseHelper(context);
databaseHelper.deleteSingleNotificationRecord(notificationRecords.get(position).getId());
notificationRecords.remove(position);
deleteNotificationListener.updateNotificationList(notificationRecords);
notifyDataSetChanged();
}
private void removeFromList (String id) {
for (NotificationDetails detail : notificationRecords) {
if (detail.getId().equalsIgnoreCase(id))
notificationRecords.remove(detail);
}
}
#Override
public int getItemCount () {
return notificationRecords.size();
}
public void expandView(final View v) {
int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) v.getParent()).getWidth(), View.MeasureSpec.EXACTLY);
int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
v.measure(matchParentMeasureSpec, wrapContentMeasureSpec);
final int targetHeight = v.getMeasuredHeight();
// Older versions of android (pre API 21) cancel animations for views with a height of 0.
v.getLayoutParams().height = 1;
v.setVisibility(View.VISIBLE);
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
v.getLayoutParams().height = interpolatedTime == 1
? ViewGroup.LayoutParams.WRAP_CONTENT
: (int)(targetHeight * interpolatedTime);
v.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
// Expansion speed of 1dp/ms
a.setDuration((int)(targetHeight / v.getContext().getResources().getDisplayMetrics().density));
v.startAnimation(a);
}
public void collapseView(final View v) {
//collapse(pos);
final int initialHeight = v.getMeasuredHeight();
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if(interpolatedTime == 1){
v.setVisibility(View.GONE);
}else{
v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
v.requestLayout();
}
}
#Override
public boolean willChangeBounds() {
return true;
}
};
// Collapse speed of 1dp/ms
a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density));
v.startAnimation(a);
}
public static class NotificationViewHolder extends RecyclerView.ViewHolder {
private TextView notificationTextView, notificationDateTextView, notificationTimeTextView, notificationTitleTextView;
private ImageView notificationImageview,expandCollapseImageView;
private ConstraintLayout parent;
public NotificationViewHolder(#NonNull View itemView) {
super(itemView);
notificationTextView = itemView.findViewById(R.id.notification_text_view);
notificationDateTextView = itemView.findViewById(R.id.notification_date_text_view);
notificationTimeTextView = itemView.findViewById(R.id.notification_time_text_view);
notificationTitleTextView = itemView.findViewById(R.id.notification_title_text_view);
notificationImageview = itemView.findViewById(R.id.notification_image_view);
expandCollapseImageView = itemView.findViewById(R.id.expand_collapse_arrow);
parent = itemView.findViewById(R.id.notification_parent);
}
}
}
UPDATE
I doesn't able to solve this issue. So instead of using RecyclerView.ItemDecoration I have used a View inside layout like below,
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:id="#+id/view_div"
android:background="#color/grey"
Doing like above solves the issue.
[This is my screen contains the issue][1]][1]
https://i.stack.imgur.com/CBY80.jpg
I am developing app to monitor IP camera using my mobile. Below is my MainActivity. I am using libVLC which uses surfaceview to show the video.
public class MainActivity extends AppCompatActivity {
Button prevButton, nextButton;
private int totalPages, currentPage;
RecyclerView recyclerView;
VideoAdapter videoAdapter;
static List<String> cameraList;
Paginator p;
#SuppressLint("AuthLeak")
private String[] vidUrlList = {
"http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",
"https://archive.org/download/ksnn_compilation_master_the_internet/ksnn_compilation_master_the_internet_512kb.mp4",
"http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",
"rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov",
"http://live.hkstv.hk.lxdns.com/live/hks/playlist.m3u8",
"rtsp://admin:admin#192.0.0.0:200/12"
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
prevButton = (Button) findViewById(R.id.prev_btn);
nextButton = (Button) findViewById(R.id.next_btn);
prevButton.setEnabled(false);
cameraList = new ArrayList<>(Arrays.asList(vidUrlList));
p = new Paginator();
totalPages = Paginator.TOTAL_NUM_ITEMS / Paginator.ITEMS_PER_PAGE;
currentPage = 0;
videoAdapter = new VideoAdapter(this, cameraList);
final RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.addItemDecoration(new GridSpacingItemDecoration(2, dpToPx(3),
true));
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(new VideoAdapter(MainActivity.this, p.generatePage(currentPage)));
nextButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
currentPage += 1;
recyclerView.setAdapter(new VideoAdapter(MainActivity.this, p.generatePage(currentPage)));
toggleButtons();
}
});
prevButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
currentPage -= 1;
recyclerView.setAdapter(new VideoAdapter(MainActivity.this, p.generatePage(currentPage)));
toggleButtons();
}
});
}
#Override
public void onPause() {
super.onPause();
}
#Override
public void onDestroy() {
super.onDestroy();
}
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
private int spanCount;
private int spacing;
private boolean includeEdge;
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
int column = position % spanCount;
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount;
outRect.right = (column + 1) * spacing / spanCount;
if (position < spanCount) {
outRect.top = spacing;
}
outRect.bottom = spacing;
} else {
outRect.left = column * spacing / spanCount;
outRect.right = spacing - (column + 1) * spacing / spanCount;
if (position >= spanCount) {
outRect.top = spacing;
}
}
}
}
private int dpToPx(int dp) {
Resources r = getResources();
return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dp, r.getDisplayMetrics()));
}
private void toggleButtons() {
if (currentPage == totalPages) {
nextButton.setEnabled(false);
prevButton.setEnabled(true);
} else if (currentPage == 0) {
prevButton.setEnabled(false);
nextButton.setEnabled(true);
} else if (currentPage >= 1 && currentPage <= totalPages) {
nextButton.setEnabled(true);
prevButton.setEnabled(true);
}
}
public static List<String> getModifyList() {
return cameraList;
}}
I used RecylerView and CardView with Paginator option for dispaying 4 cards in each page. Since i have n number of cameras to maintain i used Paginator. Below is my Paginator Class.
public class Paginator {
static List<String> arrNewList = MainActivity.getModifyList();
public static final int TOTAL_NUM_ITEMS = arrNewList.size();
public static final int ITEMS_PER_PAGE = 4;
public static final int ITEMS_REMAINING = TOTAL_NUM_ITEMS % ITEMS_PER_PAGE;
public static final int LAST_PAGE = TOTAL_NUM_ITEMS / ITEMS_PER_PAGE;
public ArrayList<String> generatePage(int currentPage) {
int startItem = currentPage * ITEMS_PER_PAGE;
int numOfData = ITEMS_PER_PAGE;
ArrayList<String> pageData = new ArrayList<>();
if (currentPage == LAST_PAGE && ITEMS_REMAINING > 0) {
for (int i = startItem; i < startItem + ITEMS_REMAINING; i++) {
pageData.add(arrNewList.get(i));
}
} else {
for (int i = startItem; i < startItem + numOfData; i++) {
pageData.add(arrNewList.get(i));
}
}
return pageData;
}}
Here i am trying to set adapter in which video is playing only at the last postion of each page. I dont know how to solve this. I am proving my adapter code below. Here i called SurfaceHolder.Callback and IVideoPlayer interfaces. Do come up with the solution.
public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.ViewHolder>
implements SurfaceHolder.Callback, IVideoPlayer {
Context mContext;
List<String> cameraList;
SurfaceHolder surfaceHolder;
private int mVideoWidth;
private int mVideoHeight;
private final static int VideoSizeChanged = -1;
//private SurfaceView mSurfaceView;
private LibVLC libvlc;
EventHandler mEventHandler;
public VideoAdapter(Context mContext, List<String> cameraList) {
this.mContext = mContext;
this.cameraList = cameraList;
}
#Override
public VideoAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.camera_adapter, parent, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(VideoAdapter.ViewHolder holder, int position) {
surfaceHolder.addCallback(this);
String urlList = cameraList.get(position);
Log.e("List1", "-->" + urlList);
createPlayer(urlList);
}
#Override
public int getItemCount() {
return cameraList.size();
}
public NativeCrashHandler.OnNativeCrashListener nativecrashListener = new NativeCrashHandler.OnNativeCrashListener() {
#Override
public void onNativeCrash() {
Log.e("vlcdebug", "nativecrash");
}
};
#Override
public void surfaceCreated(SurfaceHolder holder) {
Log.e("mylog", "surfaceCreated");
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
try {
if (libvlc != null) {
Log.e("mylog", "libvlc != null");
libvlc.attachSurface(surfaceHolder.getSurface(), this);
} else {
Log.e("mylog", "libvlc == null");
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.e("mylog", "surfaceDestroyed");
}
public class ViewHolder extends RecyclerView.ViewHolder {
private SurfaceView mSurfaceView;
public ViewHolder(View itemView) {
super(itemView);
mSurfaceView = itemView.findViewById(R.id.player_surface);
surfaceHolder = mSurfaceView.getHolder();
}
}
private void createPlayer(String vidUrlList) {
//releasePlayer();
try {
libvlc = new LibVLC();
mEventHandler = libvlc.getEventHandler();
libvlc.init(mContext);
libvlc.setHardwareAcceleration(LibVLC.HW_ACCELERATION_FULL);
libvlc.setSubtitlesEncoding("");
libvlc.setAout(LibVLC.AOUT_OPENSLES);
libvlc.setTimeStretching(true);
libvlc.setVerboseMode(true);
libvlc.setNetworkCaching(1000);
NativeCrashHandler.getInstance().setOnNativeCrashListener(
nativecrashListener);
if (LibVlcUtil.isGingerbreadOrLater())
libvlc.setVout(LibVLC.VOUT_ANDROID_WINDOW);
else
libvlc.setVout(LibVLC.VOUT_ANDROID_SURFACE);
LibVLC.restartInstance(mContext);
mEventHandler.addHandler(mHandler);
surfaceHolder.setKeepScreenOn(true);
MediaList list = libvlc.getMediaList();
list.clear();
list.add(new Media(libvlc, LibVLC.PathToURI(vidUrlList)), false);
//list.add(new Media(libvlc, LibVLC.PathToURI(media)), false);
//list.add(new Media(libvlc, LibVLC.PathToURI(media)), false);
libvlc.playIndex(0);
} catch (Exception e) {
Toast.makeText(mContext, "Error creating player!", Toast.LENGTH_SHORT).show();
}
}
private void releasePlayer() {
try {
EventHandler.getInstance().removeHandler(mHandler);
//libvlc.stop();
libvlc.detachSurface();
surfaceHolder = null;
libvlc.closeAout();
libvlc.destroy();
libvlc = null;
mVideoWidth = 0;
mVideoHeight = 0;
} catch (Exception e) {
e.printStackTrace();
}
}
private Handler mHandler = new MyHandler(this);
#Override
public void setSurfaceLayout(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den) {
Message msg = Message.obtain(mHandler, VideoSizeChanged, width,
height);
msg.sendToTarget();
}
#Override
public int configureSurface(Surface surface, int width, int height, int hal) {
Log.d("", "configureSurface: width = " + width + ", height = "
+ height);
if (LibVlcUtil.isICSOrLater() || surface == null)
return -1;
if (width * height == 0)
return 0;
if (hal != 0)
surfaceHolder.setFormat(hal);
surfaceHolder.setFixedSize(width, height);
return 1;
}
#Override
public void eventHardwareAccelerationError() {
//releasePlayer();
Toast.makeText(mContext, "Error with hardware acceleration",
Toast.LENGTH_SHORT).show();
}
#Override
public void setSurfaceSize(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den) {
}
#SuppressLint("HandlerLeak")
private class MyHandler extends Handler {
private WeakReference<VideoAdapter> mOwner;
MyHandler(VideoAdapter owner) {
mOwner = new WeakReference<>(owner);
}
#Override
public void handleMessage(Message msg) {
try {
VideoAdapter player = mOwner.get();
Log.e("mylog", "handleMessage " + msg.toString());
// Libvlc events
Bundle b = msg.getData();
Log.e("mylog", "handleMessage " + msg.getData());
switch (b.getInt("event")) {
case EventHandler.MediaPlayerEndReached:
Log.e("mylog", "MediaPlayerEndReached");
player.releasePlayer();
break;
case EventHandler.MediaPlayerPlaying:
Log.e("mylog", "MediaPlayerPlaying");
break;
case EventHandler.MediaPlayerPaused:
Log.e("mylog", "MediaPlayerPaused");
break;
case EventHandler.MediaPlayerStopped:
Log.e("mylog", "MediaPlayerStopped");
break;
case EventHandler.MediaPlayerPositionChanged:
Log.i("vlc", "MediaPlayerPositionChanged");
//VideoAdapter.this.notify();
break;
case EventHandler.MediaPlayerEncounteredError:
break;
default:
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}}
The problem is that you get your LibVLC instance using class's member varible like this
// kotlin
private libvlc: LibVLC? = null
...
override fun onBindViewHolder(...) {
libvlc = LibVLC()
...
}
which means each time you call onBindViewHolder, get a new instance of LibVLC and assign it to member variable libvlc, libvlc points to a new address of your new LibVLC instance, and the older one can not be refered again.
In other words, four list items in your RecyclerView share a same LibVLC instance, when you attach view/surface, the later visible views let your libvlc detach views from the older ones the reattach to the newest one, so only the last view can play normally.
I want to change the height of the view and decrease height all the bottom view. This works well in the fragment, but does not work in the adapter.
I do not want to squeeze the main view, I need to increase only the size of containerAvatar.
public class OrderAdapter extends ArrayAdapter {
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = layoutInflater.inflate(R.layout.open_order_item, parent, false);
containerAvatar = convertView.findViewById(R.id.container_avatar_size);
imgAvatar = convertView.findViewById(R.id.img_open_order_avatar);
Glide.with(context)
.load("http://neuronovosti.ru/wp-content/uploads/2017/12/Drosophila-Brain-1024x1024.jpg")
.into(imgAvatar);
containerAvatar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final ViewGroup.LayoutParams lp = containerAvatar.getLayoutParams();
final ResizeAnimation a = new ResizeAnimation(containerAvatar);
a.setDuration(500);
a.setParams(lp.height, lp.height * 2);
containerAvatar.startAnimation(a);
}
});
return convertView;
}
public class ResizeAnimation extends Animation {
private int startHeight;
private int deltaHeight; // distance between start and end height
private View view;
public ResizeAnimation(View v) {
this.view = v;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
view.getLayoutParams().height = (int) (startHeight + deltaHeight * interpolatedTime);
view.requestLayout();
}
public void setParams(int start, int end) {
this.startHeight = start;
deltaHeight = end - startHeight;
}
#Override
public void setDuration(long durationMillis) {
super.setDuration(durationMillis);
}
#Override
public boolean willChangeBounds() {
return true;
}
That's what you need
Try with setLayoutParams() Method of View -
private void setDimensions(View view, int width, int height){
android.view.ViewGroup.LayoutParams params = view.getLayoutParams();
params.width = width;
params.height = height;
view.setLayoutParams(params);
}
Added it on your Adapter -
public class OrderAdapter extends ArrayAdapter {
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = layoutInflater.inflate(R.layout.open_order_item, parent, false);
containerAvatar = convertView.findViewById(R.id.container_avatar_size);
imgAvatar = convertView.findViewById(R.id.img_open_order_avatar);
Glide.with(context)
.load("http://neuronovosti.ru/wp-content/uploads/2017/12/Drosophila-Brain-1024x1024.jpg")
.into(imgAvatar);
containerAvatar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
setDimensions(containerAvatar, 100, 100);
}
});
return convertView;
}
private void setDimensions(View view, int width, int height){
android.view.ViewGroup.LayoutParams params = view.getLayoutParams();
params.width = width;
params.height = height;
view.setLayoutParams(params);
}
}
if i expand any item in the recyclerview it expands fine but when i scroll down the recylerview i found other items also expanded , due to recycling it took the expanded size not the original one
public class FeedAdapter extends RecyclerView.Adapter<FeedAdapter.MyViewHolder> implements View.OnClickListener {
private LayoutInflater inflater;
private Context mcontext;
private int mOriginalHeight = 0;
private boolean mIsViewExpanded = false;
public FeedAdapter(Context context) {
inflater = LayoutInflater.from(context);
mcontext = context;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = inflater.inflate(R.layout.custom_row, viewGroup, false);
MyViewHolder holder = new MyViewHolder(view);
holder.frame.setOnClickListener(this);
return holder;
}
#Override
public void onBindViewHolder(MyViewHolder myViewHolder, int i) {
}
#Override
public int getItemCount() {
return 100;
}
#Override
public void onClick(final View v) {
if (mOriginalHeight == 0) {
mOriginalHeight = v.getHeight();
}
ValueAnimator valueAnimator;
if (v.getHeight() < (mOriginalHeight + (int) (mOriginalHeight * 1.5))) {
valueAnimator = ValueAnimator.ofInt(mOriginalHeight, mOriginalHeight + (int) (mOriginalHeight * 1.5));
} else {
valueAnimator = ValueAnimator.ofInt(mOriginalHeight + (int) (mOriginalHeight * 1.5), mOriginalHeight);
}
valueAnimator.setDuration(300);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
v.getLayoutParams().height = value.intValue();
v.requestLayout();
}
});
valueAnimator.start();
}
class MyViewHolder extends RecyclerView.ViewHolder {
FrameLayout frame;
public MyViewHolder(View itemView) {
super(itemView);
frame = (FrameLayout) itemView.findViewById(R.id.base);
}
}
}
so
how do i fix that and also is there a better way to expand and collapse items ?
I ran into the same issue. This happens because of the ViewHolderPattern.
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html#isRecyclable%28%29
Android will recycle the view, regardlessly if it's expanded or not.
So tell android that if your view is expanded, it is not recyclable.
In Your ViewHolder Class add:
public MyViewHolder(View itemView) {
super(itemView);
frame = (FrameLayout) itemView.findViewById(R.id.base);
frame.setTag(this); // to get a reference to viewholder later on
}
In Your OnClick Method add:
#Override
public void onClick(final View v) {
MyViewHolder holder = (MyViewHolder) v.getTag();
holder.setIsRecyclable(false);
if (mOriginalHeight == 0) {
mOriginalHeight = v.getHeight();
}
ValueAnimator valueAnimator;
...
}
This should solve Your Problem.
Remember to set "setIsRecyclable()" to true again after the view isn't expanded any more so that android can recycle it.
Hope I could help.
Cheers
Florestan
I have to build animation like This.
Sorry I don't have too much reputation to upload image. you can find gif file from the above link.
I have done all this and it works fine on KitKat and LollyPop only but not on the other API version. I am using this library. Can any one please figure out the actual problem
Here is my code. Thanks in advance
public abstract class AbstractSlideExpandableListAdapter extends WrapperListAdapterImpl
{
private View lastOpnUpperView = null;
ArrayList<View> upperViewsList = new ArrayList<View>();
ArrayList<View> lowerViewsList = new ArrayList<View>();
ArrayList<View> circleViewsList = new ArrayList<View>();
private View lastOpen = null;
private int lastOpenPosition = -1;
private int lastOpenItemIndex = 0;
private int animationDuration = 800;
private BitSet openItems = new BitSet();
private final SparseIntArray viewHeights = new SparseIntArray(10);
private ViewGroup parent;
public AbstractSlideExpandableListAdapter(ListAdapter wrapped)
{
super(wrapped);
lastOpenPosition = 0;
openItems.set(lastOpenPosition, true);
}
private OnItemExpandCollapseListener expandCollapseListener;
public void setItemExpandCollapseListener(OnItemExpandCollapseListener listener)
{
expandCollapseListener = listener;
}
public void removeItemExpandCollapseListener()
{
expandCollapseListener = null;
}
public interface OnItemExpandCollapseListener
{
public void onExpand(View itemView, int position);
public void onCollapse(View itemView, int position);
}
private void notifiyExpandCollapseListener(int type, View view, int position)
{
if (expandCollapseListener != null)
{
if (type == ExpandCollapseAnimation.EXPAND)
{
expandCollapseListener.onExpand(view, position);
}
else if (type == ExpandCollapseAnimation.COLLAPSE)
{
expandCollapseListener.onCollapse(view, position);
}
}
}
#Override
public View getView(int position, View view, ViewGroup viewGroup)
{
this.parent = viewGroup;
view = wrapped.getView(position, view, viewGroup);
enableFor(view, position);
return view;
}
public abstract View getExpandToggleButton(View parent);
public abstract View getExpandableView(View parent);
// upperView to expand animation for sequeeze
public abstract View getUpperView(View upperView);
// Lower view to expand and collapse
public abstract View getLowerView(View upperView);
// Get the circle view to hide and show
public abstract View getCircleView(View circleView);
/**
* Gets the duration of the collapse animation in ms. Default is 330ms. Override this method to change the default.
*
* #return the duration of the anim in ms
*/
public int getAnimationDuration()
{
return animationDuration;
}
/**
* Set's the Animation duration for the Expandable animation
*
* #param duration
* The duration as an integer in MS (duration > 0)
* #exception IllegalArgumentException
* if parameter is less than zero
*/
public void setAnimationDuration(int duration)
{
if (duration < 0)
{
throw new IllegalArgumentException("Duration is less than zero");
}
animationDuration = duration;
}
/**
* Check's if any position is currently Expanded To collapse the open item #see collapseLastOpen
*
* #return boolean True if there is currently an item expanded, otherwise false
*/
public boolean isAnyItemExpanded()
{
return (lastOpenPosition != -1) ? true : false;
}
public void enableFor(View parent, int position)
{
View more = getExpandToggleButton(parent);
View itemToolbar = getExpandableView(parent);
View upperView = getUpperView(parent);
View circleView = getCircleView(parent);
View lowerView = getLowerView(parent);
itemToolbar.measure(parent.getWidth(), parent.getHeight());
upperViewsList.add(upperView);
circleViewsList.add(circleView);
lowerViewsList.add(lowerView);
if (position == 0)
{
// lastopenUpperViewTemporary = upperView;
lastOpnUpperView = upperView;
upperView.setVisibility(View.GONE);
lowerView.setVisibility(View.GONE);
}
enableFor(more, upperView, itemToolbar, position);
itemToolbar.requestLayout();
}
private void animateListExpand(final View button, final View target, final int position)
{
target.setAnimation(null);
int type;
if (target.getVisibility() == View.VISIBLE)
{
type = ExpandCollapseAnimation.COLLAPSE;
}
else
{
type = ExpandCollapseAnimation.EXPAND;
}
// remember the state
if (type == ExpandCollapseAnimation.EXPAND)
{
openItems.set(position, true);
}
else
{
openItems.set(position, false);
}
// check if we need to collapse a different view
if (type == ExpandCollapseAnimation.EXPAND)
{
if (lastOpenPosition != -1 && lastOpenPosition != position)
{
if (lastOpen != null)
{
animateWithUpperView(lastOpen, ExpandCollapseAnimation.COLLAPSE, position);
// animateView(lastOpen, ExpandCollapseAnimation.COLLAPSE);
notifiyExpandCollapseListener(ExpandCollapseAnimation.COLLAPSE, lastOpen, lastOpenPosition);
}
openItems.set(lastOpenPosition, false);
}
lastOpen = target;
lastOpenPosition = position;
}
else if (lastOpenPosition == position)
{
lastOpenPosition = -1;
}
// animateView(target, type);
// Expand the view which was collapse
Animation anim = new ExpandCollapseAnimation(target, type);
anim.setDuration(getAnimationDuration());
target.startAnimation(anim);
this.notifiyExpandCollapseListener(type, target, position);
// }
}
private void enableFor(final View button, final View upperView, final View target, final int position)
{
// lastopenUpperViewTemporary = upperView;
if (target == lastOpen && position != lastOpenPosition)
{
// lastOpen is recycled, so its reference is false
lastOpen = null;
}
if (position == lastOpenPosition)
{
// re reference to the last view
// so when can animate it when collapsed
// lastOpen = target;
lastOpen = target;
lastOpnUpperView = upperView;
}
int height = viewHeights.get(position, -1);
if (height == -1)
{
viewHeights.put(position, target.getMeasuredHeight());
updateExpandable(target, position);
}
else
{
updateExpandable(target, position);
}
button.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(final View view)
{
System.out.println("Position: " + position);
if (lastOpenPosition == position)
{
return;
}
System.out.println("Upper View: " + upperView);
Animation anim = new ExpandCollapseUpperViewAnimation(upperViewsList.get(position), ExpandCollapseUpperViewAnimation.COLLAPSE);
anim.setDuration(800);
anim.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
circleViewsList.get(position).setVisibility(View.GONE);
}
#Override
public void onAnimationRepeat(Animation animation)
{
// TODO Auto-generated method stub
}
#Override
public void onAnimationEnd(Animation animation)
{
// TODO Auto-generated method stub
// upperViewsList.get(position).setVisibility(View.VISIBLE);
animateListExpand(button, target, position);
}
});
upperViewsList.get(position).startAnimation(anim);
// Lower animation
Animation lowerAnim = new ExpandCollapseUpperViewAnimation(lowerViewsList.get(position), ExpandCollapseUpperViewAnimation.COLLAPSE);
lowerAnim.setDuration(800);
lowerViewsList.get(position).startAnimation(lowerAnim);
}
});
}
private void updateExpandable(View target, int position)
{
final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) target.getLayoutParams();
if (openItems.get(position))
{
target.setVisibility(View.VISIBLE);
params.bottomMargin = 0;
}
else
{
target.setVisibility(View.GONE);
params.bottomMargin = 0 - viewHeights.get(position);
}
}
/**
* Performs either COLLAPSE or EXPAND animation on the target view
*
* #param target
* the view to animate
* #param type
* the animation type, either ExpandCollapseAnimation.COLLAPSE or ExpandCollapseAnimation.EXPAND
*/
private void animateView(final View target, final int type)
{
Animation anim = new ExpandCollapseAnimation(target, type);
anim.setDuration(getAnimationDuration());
anim.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
}
#Override
public void onAnimationRepeat(Animation animation)
{
}
#Override
public void onAnimationEnd(Animation animation)
{
System.out.println("Animation End");
if (type == ExpandCollapseAnimation.EXPAND)
{
if (parent instanceof ListView)
{
ListView listView = (ListView) parent;
int movement = target.getBottom();
Rect r = new Rect();
boolean visible = target.getGlobalVisibleRect(r);
Rect r2 = new Rect();
listView.getGlobalVisibleRect(r2);
if (!visible)
{
listView.smoothScrollBy(movement, getAnimationDuration());
}
else
{
if (r2.bottom == r.bottom)
{
listView.smoothScrollBy(movement, getAnimationDuration());
}
}
}
}
}
});
target.startAnimation(anim);
}
private void animateWithUpperView(final View target, final int type, final int position)
{
Animation anim = new ExpandCollapseAnimation(target, type);
anim.setDuration(getAnimationDuration());
anim.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
}
#Override
public void onAnimationRepeat(Animation animation)
{
}
#Override
public void onAnimationEnd(Animation animation)
{
// upperViewsList.get(lastOpenItemForUpperView).setVisibility(View.VISIBLE);
// lastOpenItemForUpperView = position;
Animation expandItemAniamtion = new ExpandCollapseLowerViewAnimation(upperViewsList.get(lastOpenItemIndex), ExpandCollapseUpperViewAnimation.EXPAND);
expandItemAniamtion.setDuration(800);
expandItemAniamtion.setAnimationListener(new AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
}
#Override
public void onAnimationRepeat(Animation animation)
{
// TODO Auto-generated method stub
}
#Override
public void onAnimationEnd(Animation animation)
{
circleViewsList.get(lastOpenItemIndex).setVisibility(View.VISIBLE);
lastOpenItemIndex = position;
}
});
// Lower view animation
Animation lowerAnim = new ExpandCollapseLowerViewAnimation(lowerViewsList.get(lastOpenItemIndex), ExpandCollapseUpperViewAnimation.EXPAND);
lowerAnim.setDuration(800);
upperViewsList.get(lastOpenItemIndex).startAnimation(expandItemAniamtion);
lowerViewsList.get(lastOpenItemIndex).startAnimation(lowerAnim);
}
});
target.startAnimation(anim);
}
/**
* Closes the current open item. If it is current visible it will be closed with an animation.
*
* #return true if an item was closed, false otherwise
*/
public boolean collapseLastOpen()
{
if (isAnyItemExpanded())
{
// if visible animate it out
if (lastOpen != null)
{
animateView(lastOpen, ExpandCollapseAnimation.COLLAPSE);
}
openItems.set(lastOpenPosition, false);
lastOpenPosition = -1;
return true;
}
return false;
}
public Parcelable onSaveInstanceState(Parcelable parcelable)
{
SavedState ss = new SavedState(parcelable);
ss.lastOpenPosition = this.lastOpenPosition;
ss.openItems = this.openItems;
return ss;
}
public void onRestoreInstanceState(SavedState state)
{
if (state != null)
{
this.lastOpenPosition = state.lastOpenPosition;
this.openItems = state.openItems;
}
}
/**
* Utility methods to read and write a bitset from and to a Parcel
*/
private static BitSet readBitSet(Parcel src)
{
BitSet set = new BitSet();
if (src == null)
{
return set;
}
int cardinality = src.readInt();
for (int i = 0; i < cardinality; i++)
{
set.set(src.readInt());
}
return set;
}
private static void writeBitSet(Parcel dest, BitSet set)
{
int nextSetBit = -1;
if (dest == null || set == null)
{
return; // at least dont crash
}
dest.writeInt(set.cardinality());
while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1)
{
dest.writeInt(nextSetBit);
}
}
/**
* The actual state class
*/
static class SavedState extends View.BaseSavedState
{
public BitSet openItems = null;
public int lastOpenPosition = -1;
SavedState(Parcelable superState)
{
super(superState);
}
private SavedState(Parcel in)
{
super(in);
lastOpenPosition = in.readInt();
openItems = readBitSet(in);
}
#Override
public void writeToParcel(Parcel out, int flags)
{
super.writeToParcel(out, flags);
out.writeInt(lastOpenPosition);
writeBitSet(out, openItems);
}
// required field that makes Parcelables from a Parcel
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>()
{
public SavedState createFromParcel(Parcel in)
{
return new SavedState(in);
}
public SavedState[] newArray(int size)
{
return new SavedState[size];
}
};
}
public static Animation ExpandOrCollapseView(final View v, final boolean expand)
{
try
{
Method m = v.getClass().getDeclaredMethod("onMeasure", int.class, int.class);
m.setAccessible(true);
m.invoke(v, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(((View) v.getParent()).getMeasuredWidth(), MeasureSpec.AT_MOST));
}
catch (Exception e)
{
e.printStackTrace();
}
final int initialHeight = v.getMeasuredHeight();
if (expand)
{
v.getLayoutParams().height = 0;
}
else
{
v.getLayoutParams().height = initialHeight;
}
v.setVisibility(View.VISIBLE);
Animation a = new Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
int newHeight = 0;
if (expand)
{
newHeight = (int) (initialHeight * interpolatedTime);
}
else
{
newHeight = (int) (initialHeight * (1 - interpolatedTime));
}
v.getLayoutParams().height = newHeight;
v.requestLayout();
if (interpolatedTime == 1 && !expand)
v.setVisibility(View.GONE);
}
#Override
public boolean willChangeBounds()
{
return true;
}
};
a.setDuration(1000);
return a;
}}
Luckily I have found the solution. On pre-kitkat the upperView on list item which was gone does not forcelly animate but on kitkat and so on it forcelly animate from gone to visible.
So as the solution we must give a layer of view between of list item and upper view (Which we have to animate).
In this way it will work like charm.