Android measure view visible area - android

I have been searching a lot of similar answer from here, but none can work accurately. I want to calculate the visible area of a custom view, the view can be blocked by the screen edge, or block by the edge of scroll view, let see the picture below:
As above, black color is my screen, red color is my custom view and scroll up a bit, I want to measure area of B.
As above, black color is my screen, red color is my custom view, blue color is scroll view. Custom view is child of the scroll view and it is scroll up a bit. I want to measure area of B.
1) I have tried, View.getWindowVisibleDisplayFrame, View.getLocalVisibleRect, View.getGlobalVisibleRect, but none of it work accurately. First glance they looks good, but when I scroll my view disappear from screen, somehow, it show me the full height and width of the view, which the view is not even displayed within the screen.
2) I tried View.getLocationOnScreen() and getLocationInWindow() to calculate the offset manually, get XY coordination and plus/minus the view's (and screen) height and width, but found it not easy too, because the top of screen always have extra menu bar or etc, and will mess out with the result.
3) Although this is not likely in my situation, I want to know, if there is a absolute layout on top of my view and partially block it, can I still find out the area? (both layout are in same activity)
My question is, is there any easy and accurate way to calculate the area I want?

Ok, I found the answer from one of the open source Ad framework:
/**
* Whether the view is at least certain % visible
*/
boolean isVisible(#Nullable final View rootView, #Nullable final View view, final int minPercentageViewed) {
// ListView & GridView both call detachFromParent() for views that can be recycled for
// new data. This is one of the rare instances where a view will have a null parent for
// an extended period of time and will not be the main window.
// view.getGlobalVisibleRect() doesn't check that case, so if the view has visibility
// of View.VISIBLE but it's group has no parent it is likely in the recycle bin of a
// ListView / GridView and not on screen.
if (view == null || view.getVisibility() != View.VISIBLE || rootView.getParent() == null) {
return false;
}
if (!view.getGlobalVisibleRect(mClipRect)) {
// Not visible
return false;
}
// % visible check - the cast is to avoid int overflow for large views.
final long visibleViewArea = (long) mClipRect.height() * mClipRect.width();
final long totalViewArea = (long) view.getHeight() * view.getWidth();
if (totalViewArea <= 0) {
return false;
}
return 100 * visibleViewArea >= minPercentageViewed * totalViewArea;
}
I made a mistake while I am using View.getGlobalVisibleRect, when the view disappear from the screen, this method will return false, although the mClipRect object still providing value. Above is the correct way in using it.

During implementing a new feature "ViewHierarchy" in my work at Instabug I face the same problem and fix this problem through the below code
This is util class that does all the logic
public class ViewFrameInspector {
private static final String KEY_X = "x";
private static final String KEY_Y = "y";
private static final String KEY_W = "w";
private static final String KEY_H = "h";
/**
* Method emit inspected ViewFrame of passed view, the emit ViewFrame contains inspected ViewFrames fot its children and children of the children and so on
* by converting the emitted ViewFrame to list of View Frames you can find the a specific view and its frame with easily way
*
* #param view the root view
* #return return ViewFrame observable
*/
public static Observable<ViewFrame> inspectRootViewFrameRx(final View view) {
return Observable.defer(new Func0<Observable<ViewFrame>>() {
#Override
public Observable<ViewFrame> call() {
ViewFrame rootViewFrame = new ViewFrame();
rootViewFrame.setRoot(true);
rootViewFrame.setView(view);
return Observable.just(inspectVisibleViewFrame(rootViewFrame));
}
});
}
private static ViewFrame inspectVisibleViewFrame(final ViewFrame viewFrame) {
if (viewFrame.getView().getVisibility() == View.VISIBLE)
try {
viewFrame.setId(inspectViewResourceId(viewFrame.getView().getContext(), viewFrame.getView().getId()));
viewFrame.setType(ViewFrameInspector.inspectViewType(viewFrame.getView()));
viewFrame.setOriginalRect(ViewFrameInspector.inspectViewOriginalRect(viewFrame.getView()));
viewFrame.setVisibleRect(ViewFrameInspector.inspectViewVisibleRect(viewFrame));
viewFrame.setFrame(ViewFrameInspector.inspectViewFrame(viewFrame));
// inspect view children if exist
if (viewFrame.getView() instanceof ViewGroup) {
viewFrame.setHasChildren(true);
inspectViewChildren(viewFrame);
} else {
viewFrame.setHasChildren(false);
}
} catch (JSONException e) {
Log.e(ActivityViewInspector.class.getSimpleName(), "inspect view frame got error: " + e.getMessage() + ",view id:" + viewFrame.getId() + ", time in MS: " + System.currentTimeMillis(), e);
}
return viewFrame;
}
private static void inspectViewChildren(ViewFrame parentViewFrame) throws JSONException {
if (parentViewFrame.getView() instanceof ViewGroup) {
ViewGroup parent = (ViewGroup) parentViewFrame.getView();
for (int i = 0; i < parent.getChildCount(); i++) {
ViewFrame childViewFrame = new ViewFrame();
childViewFrame.setRoot(false);
childViewFrame.setView(parent.getChildAt(i));
childViewFrame.setParent(parentViewFrame);
parentViewFrame.addNode(inspectVisibleViewFrame(childViewFrame));
}
}
}
private static String inspectViewType(View view) {
return view.getClass().getSimpleName();
}
private static String inspectViewResourceId(Context context, int id) throws JSONException {
try {
return context != null && context.getResources() != null && context.getResources().getResourceEntryName(id) != null ?
context.getResources().getResourceEntryName(id) : String.valueOf(id);
} catch (Resources.NotFoundException e) {
return String.valueOf(id);
}
}
private static Rect inspectViewOriginalRect(View view) {
int[] locationOnScreen = new int[2];
view.getLocationOnScreen(locationOnScreen);
return new Rect(locationOnScreen[0],
locationOnScreen[1],
locationOnScreen[0] + view.getWidth(),
locationOnScreen[1] + view.getHeight());
}
private static Rect inspectViewVisibleRect(ViewFrame viewFrame) {
if (viewFrame.isRoot()) {
return viewFrame.getOriginalRect();
} else {
Rect viewVisibleRect = new Rect(
viewFrame.getOriginalRect().left,
viewFrame.getOriginalRect().top,
viewFrame.getOriginalRect().right,
viewFrame.getOriginalRect().bottom);
Rect parentAvailableVisibleRect = new Rect(
inspectViewAvailableX(viewFrame.getParent()),
inspectViewAvailableY(viewFrame.getParent()),
inspectViewAvailableRight(viewFrame.getParent()),
inspectViewAvailableBottom(viewFrame.getParent()));
if (viewVisibleRect.intersect(parentAvailableVisibleRect)) {
return viewVisibleRect;
} else {
return new Rect(0, 0, 0, 0);
}
}
}
private static int inspectViewAvailableX(ViewFrame viewFrame) {
int visibleLeft, paddingLeft, originalLeft;
visibleLeft = viewFrame.getVisibleRect().left;
paddingLeft = viewFrame.getView().getPaddingLeft();
originalLeft = viewFrame.getOriginalRect().left;
if (paddingLeft == 0) {
return visibleLeft;
} else {
if (visibleLeft > (originalLeft + paddingLeft)) {
return visibleLeft;
} else {
return originalLeft + paddingLeft;
}
}
}
private static int inspectViewAvailableY(ViewFrame viewFrame) {
int visibleTop, paddingTop, originalTop;
visibleTop = viewFrame.getVisibleRect().top;
paddingTop = viewFrame.getView().getPaddingTop();
originalTop = viewFrame.getOriginalRect().top;
if (paddingTop == 0) {
return visibleTop;
} else {
if (visibleTop > (originalTop + paddingTop)) {
return visibleTop;
} else {
return originalTop + paddingTop;
}
}
}
private static int inspectViewAvailableRight(ViewFrame viewFrame) {
int visibleRight, paddingRight, originalRight;
visibleRight = viewFrame.getVisibleRect().right;
paddingRight = viewFrame.getView().getPaddingRight();
originalRight = viewFrame.getOriginalRect().right;
if (paddingRight == 0) {
return visibleRight;
} else {
if (visibleRight < (originalRight - paddingRight)) {
return visibleRight;
} else {
return originalRight - paddingRight;
}
}
}
private static int inspectViewAvailableBottom(ViewFrame viewFrame) {
int visibleBottom, paddingBottom, originalBottom;
visibleBottom = viewFrame.getVisibleRect().bottom;
paddingBottom = viewFrame.getView().getPaddingBottom();
originalBottom = viewFrame.getOriginalRect().bottom;
if (paddingBottom == 0) {
return visibleBottom;
} else {
if (visibleBottom < (originalBottom - paddingBottom)) {
return visibleBottom;
} else {
return originalBottom - paddingBottom;
}
}
}
private static JSONObject inspectViewFrame(ViewFrame viewFrame) throws JSONException {
return new JSONObject().put(KEY_X, viewFrame.getVisibleRect().left)
.put(KEY_Y, viewFrame.getVisibleRect().top)
.put(KEY_W, viewFrame.getVisibleRect().width())
.put(KEY_H, viewFrame.getVisibleRect().height());
}
public static List<ViewFrame> convertViewHierarchyToList(ViewFrame viewFrame) {
ArrayList<ViewFrame> viewFrameHierarchies = new ArrayList<>();
if (viewFrame != null) {
viewFrameHierarchies.add(viewFrame);
if (viewFrame.hasChildren()) {
for (ViewFrame childViewHierarchy : viewFrame.getNodes()) {
viewFrameHierarchies.addAll(convertViewHierarchyToList(childViewHierarchy));
}
}
}
return viewFrameHierarchies;
}
}
This is model class that hold all data related to inspected views
public class ViewFrame {
private String id;
private String type;
private JSONObject frame;
private ViewFrame parent;
private ArrayList<ViewFrame> nodes;
private boolean hasChildren;
private boolean isRoot;
private Rect originalRect;
private Rect visibleRect;
private View view;
public ViewFrame() {
nodes = new ArrayList<>();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public JSONObject getFrame() {
return frame;
}
public void setFrame(JSONObject frame) {
this.frame = frame;
}
public ViewFrame getParent() {
return parent;
}
public void setParent(ViewFrame parent) {
this.parent = parent;
}
public ArrayList<ViewFrame> getNodes() {
return nodes;
}
public void addNode(ViewFrame childViewHierarchy) {
nodes.add(childViewHierarchy);
}
public boolean hasChildren() {
return hasChildren;
}
public void setHasChildren(boolean hasChildren) {
this.hasChildren = hasChildren;
}
public boolean isRoot() {
return isRoot;
}
public void setRoot(boolean root) {
isRoot = root;
}
public Rect getVisibleRect() {
return visibleRect;
}
public void setVisibleRect(Rect visibleRect) {
this.visibleRect = visibleRect;
}
public Rect getOriginalRect() {
return originalRect;
}
public void setOriginalRect(Rect originalRect) {
this.originalRect = originalRect;
}
public View getView() {
return view;
}
public void setView(View view) {
this.view = view;
}
}
Hope this code help you also

Related

Not working Notifydatasetchange on RecyclerView with center selection Horizontal Scrollview?

Auto Scrolling is issue & also move to some specific position using code is difficult.
I am making two recyclerView dependent to each other with the Horizontal Scroll and center selection.
So my problem is using the method of Notifydatasetchanged and reseting recyclerView postion to 0 and it's scrolling selection range because it's returning wrong index...
When i want to get center selection index after changing data.
I am using below example to achieve this with some edits.
Get center visible item of RecycleView when scrolling
I need to change the data on scroll of First recyclerView Adapter to second recyclerView Adapter with data change.
But scrollview set the position in first position
I tried the notifyItemRangeChanged(int, int)
notifyItemRangeInserted(int, int) methods...
Detail Explanation : I am changing the type and reset the value of Look scrollview. I need to change the selected position of bottom scrollview. Specially I can't get the center position by changing data. Means I if i am notifying the adapter than index will remain as it is. I need to do work it like normal adapter after reset data.
Thanks in advance.
public void getRecyclerview_Type() {
final RecyclerView recyclerView_Type = (RecyclerView) findViewById(R.id.recycleView);
if (recyclerView_Type != null) {
recyclerView_Type.postDelayed(new Runnable() {
#Override
public void run() {
setTypeValue();
}
}, 300);
recyclerView_Type.postDelayed(new Runnable() {
#Override
public void run() {
// recyclerView_Type.smoothScrollToPosition(Type_Adapter.getItemCount() - 1);
setTypeValue();
}
}, 5000);
}
ViewTreeObserver treeObserver = recyclerView_Type.getViewTreeObserver();
treeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
recyclerView_Type.getViewTreeObserver().removeOnPreDrawListener(this);
finalWidthDate = recyclerView_Type.getMeasuredWidth();
itemWidthDate = getResources().getDimension(R.dimen.item_dob_width_padding);
paddingDate = (finalWidthDate - itemWidthDate) / 2;
firstItemWidthDate = paddingDate;
allPixelsDate = 0;
final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView_Type.setLayoutManager(dateLayoutManager);
recyclerView_Type.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
synchronized (this) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
calculatePositionAndScroll_Type(recyclerView);
}
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
allPixelsDate += dx;
}
});
if (mTypeBeanArrayList == null) {
mTypeBeanArrayList = new ArrayList<>();
}
getMedicationType();
Type_Adapter = new Medication_Type_RecyclerAdapter(Add_Reminder_medicationlook_Activity.this, mTypeBeanArrayList, (int) firstItemWidthDate);
recyclerView_Type.setAdapter(Type_Adapter);
Type_Adapter.setSelecteditem(Type_Adapter.getItemCount() - 1);
return true;
}
});
}
private void getMedicationType() {
for (int i = 0; i < mTypeBeanArrayList.size(); i++) {
Medication_TypeBean medication_typeBean = mTypeBeanArrayList.get(i);
Log.print("Image name :" +medication_typeBean.getType_image_name());
if (i == 0 || i == (mTypeBeanArrayList.size() - 1)) {
medication_typeBean.setType(VIEW_TYPE_PADDING);
} else {
medication_typeBean.setType(VIEW_TYPE_ITEM);
}
mTypeBeanArrayList.set(i, medication_typeBean);
}
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void calculatePositionAndScroll_Type(RecyclerView recyclerView) {
int expectedPositionDate = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
if (expectedPositionDate == -1) {
expectedPositionDate = 0;
} else if (expectedPositionDate >= recyclerView.getAdapter().getItemCount() - 2) {
expectedPositionDate--;
}
scrollListToPosition_Type(recyclerView, expectedPositionDate);
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void scrollListToPosition_Type(RecyclerView recyclerView, int expectedPositionDate) {
float targetScrollPosDate = expectedPositionDate * itemWidthDate + firstItemWidthDate - paddingDate;
float missingPxDate = targetScrollPosDate - allPixelsDate;
if (missingPxDate != 0) {
recyclerView.smoothScrollBy((int) missingPxDate, 0);
}
setTypeValue();
}
private void setTypeValue() {
int expectedPositionDateColor = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
int setColorDate = expectedPositionDateColor + 1;
Type_Adapter.setSelecteditem(setColorDate);
mTxt_type_name.setText(mTypeBeanArrayList.get(setColorDate).getMedication_type_name());
mSELECTED_TYPE_ID = setColorDate;
//NotifyLookChangetoType(setColorDate);
}
//Type Adapter
public class Medication_Type_RecyclerAdapter extends RecyclerView.Adapter<Medication_Type_RecyclerAdapter.ViewHolder> {
private ArrayList<Medication_TypeBean> medication_typeBeanArrayList;
private static final int VIEW_TYPE_PADDING = 1;
private static final int VIEW_TYPE_ITEM = 2;
private int paddingWidthDate = 0;
private Context mContext;
private int selectedItem = -1;
public Medication_Type_RecyclerAdapter(Context context, ArrayList<Medication_TypeBean> dateData, int paddingWidthDate) {
this.medication_typeBeanArrayList = dateData;
this.paddingWidthDate = paddingWidthDate;
this.mContext = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ITEM) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_medication_type,
parent, false);
return new ViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_medication_type,
parent, false);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
layoutParams.width = paddingWidthDate;
view.setLayoutParams(layoutParams);
return new ViewHolder(view);
}
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Medication_TypeBean medication_typeBean = mTypeBeanArrayList.get(position);
if (getItemViewType(position) == VIEW_TYPE_ITEM) {
// holder.mTxt_Type.setText(medication_typeBean.getMedication_type_name());
//holder.mTxt_Type.setVisibility(View.VISIBLE);
holder.mImg_medication.setVisibility(View.VISIBLE);
int d = R.drawable.ic_type_pill;
try {
//Due to Offline requirements we do code like this get the images from our res folder
if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_pill")) {
d = R.drawable.ic_type_pill;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_patch")) {
d = R.drawable.ic_type_patch;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_capsule")) {
d = R.drawable.ic_type_capsule;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_ring")) {
d = R.drawable.ic_type_ring;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_inhaler")) {
d = R.drawable.ic_type_inhaler;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_spray")) {
d = R.drawable.ic_type_spray;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_bottle")) {
d = R.drawable.ic_type_bottle;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_drop")) {
d = R.drawable.ic_type_drop;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_pessaries")) {
d = R.drawable.ic_type_pessaries;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_sachets")) {
d = R.drawable.ic_type_sachets;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_tube")) {
d = R.drawable.ic_type_tube;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_suppository")) {
d = R.drawable.ic_type_suppository;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_injaction")) {
d = R.drawable.ic_type_injaction;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_spoon")) {
d = R.drawable.ic_type_spoon;
} else if (medication_typeBean.getType_image_name().equalsIgnoreCase("ic_type_powder")) {
d = R.drawable.ic_type_powder;
} else {
d = R.drawable.ic_type_pill;
}
Bitmap icon = BitmapFactory.decodeResource(mContext.getResources(),
d);
holder.mImg_medication.setImageBitmap(icon);
} catch (Exception e) {
Log.print(e);
}
// BitmapDrawable ob = new BitmapDrawable(mContext.getResources(), icon);
// img.setBackgroundDrawable(ob);
// holder.mImg_medication.setBackground(ob);
// Log.print("Type Adapter", "default " + position + ", selected " + selectedItem);
if (position == selectedItem) {
Log.print("Type adapter", "center" + position);
// holder.mTxt_Type.setTextColor(Color.parseColor("#76FF03"));
//holder.mImg_medication.setColorFilter(Color.GREEN);
// holder.mTxt_Type.setTextSize(35);
holder.mImg_medication.getLayoutParams().height = (int) mContext.getResources().getDimension(R.dimen.item_dob_width) + 10;
holder.mImg_medication.getLayoutParams().width = (int) mContext.getResources().getDimension(R.dimen.item_dob_width) + 10;
} else {
holder.mImg_medication.getLayoutParams().height = (int) mContext.getResources().getDimension(R.dimen.item_dob_width) - 10;
holder.mImg_medication.getLayoutParams().width = (int) mContext.getResources().getDimension(R.dimen.item_dob_width) - 10;
// holder.mTxt_Type.setTextColor(Color.WHITE);
//holder.mImg_medication.setColorFilter(null);
// holder.mTxt_Type.setVisibility(View.INVISIBLE);
// holder.mTxt_Type.setTextSize(18);
// holder.mImg_medication.getLayoutParams().height = 70;
// holder.mImg_medication.getLayoutParams().width = 70;
}
} else {
holder.mImg_medication.getLayoutParams().height = (int) mContext.getResources().getDimension(R.dimen.item_dob_width) - 10;
holder.mImg_medication.getLayoutParams().width = (int) mContext.getResources().getDimension(R.dimen.item_dob_width) - 10;
// holder.mTxt_Type.setVisibility(View.INVISIBLE);
holder.mImg_medication.setVisibility(View.INVISIBLE);
}
}
public void setSelecteditem(int selecteditem) {
this.selectedItem = selecteditem;
notifyDataSetChanged();
if (medication_lookBeanArrayList != null && Look_Adapter != null) {
NotifyLookChangetoType(selecteditem);
}
}
public int getSelecteditem() {
return selectedItem;
}
#Override
public int getItemCount() {
return medication_typeBeanArrayList.size();
}
#Override
public int getItemViewType(int position) {
Medication_TypeBean medication_typeBean = medication_typeBeanArrayList.get(position);
if (medication_typeBean.getType() == VIEW_TYPE_PADDING) {
return VIEW_TYPE_PADDING;
} else {
return VIEW_TYPE_ITEM;
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
//public TextView mTxt_Type;
public ImageView mImg_medication;
public ViewHolder(View itemView) {
super(itemView);
// mTxt_Type = (TextView) itemView.findViewById(R.id.mTxt);
mImg_medication = (ImageView) itemView.findViewById(R.id.mImg_medication);
}
}
}
//Type Adapter ends ************************************************
I am not sure I get you properly but you want to change the data of one recyclerview and reset the value of other recyclerview
Try using recyclerView.scrollToPosition(INDEX_YOU_WANT_TO_SCROLL_TO);
IF YOU WANT TO ACHIEVE THE CENTER POSTION OF THE DATA USE
recyclerview.scrollToPosition(arraylist.size()/2);
arrayList in which your drawable data is stored

Android: Animation works in KitKat and LolliPop but not on other API versions

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.

Draw mathematical function with "androidPlot" library

I want to draw math-function like y=x^2+1 with androidPlot library.I have "SimpleXYPlot". It works but I don't know how to change it from sin to my function.
Here's the code:
public class DynamicXYPlotActivity extends Activity {
// redraws a plot whenever an update is received:
private class MyPlotUpdater implements Observer {
Plot plot;
public MyPlotUpdater(Plot plot) {
this.plot = plot;
}
#Override
public void update(Observable o, Object arg) {
plot.redraw();
}
}
private XYPlot dynamicPlot;
private MyPlotUpdater plotUpdater;
SampleDynamicXYDatasource data;
private Thread myThread;
#Override
public void onCreate(Bundle savedInstanceState) {
// android boilerplate stuff
super.onCreate(savedInstanceState);
setContentView(R.layout.dynamicxyplot_example);
// get handles to our View defined in layout.xml:
dynamicPlot = (XYPlot) findViewById(R.id.dynamicXYPlot);
plotUpdater = new MyPlotUpdater(dynamicPlot);
// only display whole numbers in domain labels
dynamicPlot.getGraphWidget().setDomainValueFormat(new DecimalFormat("0"));
// getInstance and position datasets:
data = new SampleDynamicXYDatasource();
SampleDynamicSeries sine1Series = new SampleDynamicSeries(data, 0, "Sine 1");
SampleDynamicSeries sine2Series = new SampleDynamicSeries(data, 1, "Sine 2");
LineAndPointFormatter formatter1 = new LineAndPointFormatter( Color.rgb(0, 0, 0), null, null, null );
formatter1.getLinePaint().setStrokeJoin(Paint.Join.ROUND);
formatter1.getLinePaint().setStrokeWidth(10);
dynamicPlot.addSeries( sine1Series,formatter1 );
LineAndPointFormatter formatter2 = new LineAndPointFormatter(Color.rgb(0, 0, 200), null, null, null);
formatter2.getLinePaint().setStrokeWidth(10);
formatter2.getLinePaint().setStrokeJoin(Paint.Join.ROUND);
//formatter2.getFillPaint().setAlpha(220);
dynamicPlot.addSeries(sine2Series, formatter2);
// hook up the plotUpdater to the data model:
data.addObserver(plotUpdater);
// thin out domain tick labels so they dont overlap each other:
dynamicPlot.setDomainStepMode(XYStepMode.INCREMENT_BY_VAL);
dynamicPlot.setDomainStepValue(5);
dynamicPlot.setRangeStepMode(XYStepMode.INCREMENT_BY_VAL);
dynamicPlot.setRangeStepValue(10);
dynamicPlot.setRangeValueFormat(new DecimalFormat("###.#"));
// uncomment this line to freeze the range boundaries:
dynamicPlot.setRangeBoundaries(-100, 100, BoundaryMode.FIXED);
// create a dash effect for domain and range grid lines:
DashPathEffect dashFx = new DashPathEffect(
new float[] {PixelUtils.dpToPix(3), PixelUtils.dpToPix(3)}, 0);
dynamicPlot.getGraphWidget().getDomainGridLinePaint().setPathEffect(dashFx);
dynamicPlot.getGraphWidget().getRangeGridLinePaint().setPathEffect(dashFx);
}
#Override
public void onResume() {
// kick off the data generating thread:
myThread = new Thread(data);
myThread.start();
super.onResume();
}
#Override
public void onPause() {
data.stopThread();
super.onPause();
}
class SampleDynamicXYDatasource implements Runnable {
// encapsulates management of the observers watching this datasource for update events:
class MyObservable extends Observable {
#Override
public void notifyObservers() {
setChanged();
super.notifyObservers();
}
}
private static final double FREQUENCY = 5; // larger is lower frequency
private static final int MAX_AMP_SEED = 100; //100
private static final int MIN_AMP_SEED = 10; //10
private static final int AMP_STEP = 1;
public static final int SINE1 = 0;
public static final int SINE2 = 1;
private static final int SAMPLE_SIZE = 30;
private int phase = 0;
private int sinAmp = 1;
private MyObservable notifier;
private boolean keepRunning = false;
{
notifier = new MyObservable();
}
public void stopThread() {
keepRunning = false;
}
//#Override
public void run() {
try {
keepRunning = true;
boolean isRising = true;
while (keepRunning) {
Thread.sleep(100); // decrease or remove to speed up the refresh rate.
phase++;
if (sinAmp >= MAX_AMP_SEED) {
isRising = false;
} else if (sinAmp <= MIN_AMP_SEED) {
isRising = true;
}
if (isRising) {
sinAmp += AMP_STEP;
} else {
sinAmp -= AMP_STEP;
}
notifier.notifyObservers();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public int getItemCount(int series) {
return SAMPLE_SIZE;
}
public Number getX(int series, int index) {
if (index >= SAMPLE_SIZE) {
throw new IllegalArgumentException();
}
return index;
}
public Number getY(int series, int index) {
if (index >= SAMPLE_SIZE) {
throw new IllegalArgumentException();
}
double angle = (index + (phase))/FREQUENCY;
double amp = sinAmp * Math.sin(angle);
switch (series) {
case SINE1:
return amp;
case SINE2:
return -amp;
default:
throw new IllegalArgumentException();
}
}
public void addObserver(Observer observer) {
notifier.addObserver(observer);
}
public void removeObserver(Observer observer) {
notifier.deleteObserver(observer);
}
}
class SampleDynamicSeries implements XYSeries {
private SampleDynamicXYDatasource datasource;
private int seriesIndex;
private String title;
public SampleDynamicSeries(SampleDynamicXYDatasource datasource, int seriesIndex, String title) {
this.datasource = datasource;
this.seriesIndex = seriesIndex;
this.title = title;
}
#Override
public String getTitle() {
return title;
}
#Override
public int size() {
return datasource.getItemCount(seriesIndex);
}
#Override
public Number getX(int index) {
return datasource.getX(seriesIndex, index);
}
#Override
public Number getY(int index) {
return datasource.getY(seriesIndex, index);
}
}
}
=======================================================
After what "Nick" said and other minor addition, I got this result:
but as we know :
https://www.google.com/search?q=y%3Dx%5E2%2B1&oq=y%3Dx%5E2%2B1&aqs=chrome..69i57j0l5.7056j0j7&sourceid=chrome&es_sm=93&ie=UTF-8
Now how to make the left side?
Using the code above can modify SampleDynamicXYDatasource to do what you want. All that code does is generate some data in a sine pattern. I don't know how your x values are going to be generated so here's a modified SampleDynamicXYDatasource.getY(...) that just uses the original code where x=index above and uses your function to generate the y-values:
public Number getY(int series, int index) {
if (index >= SAMPLE_SIZE) {
throw new IllegalArgumentException();
}
Number x = getX(series, index);
double y = Math.pow(x.doubleValue(), 2) + 1;
switch (series) {
case SINE1:
return y;
case SINE2:
return -y;
default:
throw new IllegalArgumentException();
}
}
You'll notice that when you make this change, the plot appears to no longer be animated. (it actually still is but that's besides the point) This is because y is now purely a function of x and the x values never change.
As far as how to stop the animation, the plot redraws whenever plot.redraw() is called, which in the example above is in response to an event being continuously fired by events generated by the thread being run on the Runnable instance of SampleDynamicXYDatasource. Using the example above, the simplest way to stop the animation is to replace:
#Override
public void onResume() {
// kick off the data generating thread:
myThread = new Thread(data);
myThread.start();
super.onResume();
}
with:
#Override
public void onResume() {
dynamicPlot.redraw();
super.onResume();
}
Try replace this>>
public Number getX(int series, int index) {
if (index >= SAMPLE_SIZE) {
throw new IllegalArgumentException();
}
return index;
}
To this>>
public Number getX(int series, int index) {
if (index >= SAMPLE_SIZE) {
throw new IllegalArgumentException();
}
return index - 15;
}

In Android how to synchronize scrollview with different scrolling speeds?

I have some dependent scrollview means (when I scroll one view others will also scroll). I am able to do it properly, but now I want to manage the speed of scroll means (when I scroll my scrollview1 than scrollview2 should scroll +10 and scrollview3 should scroll +20 or whatever speed) same for other (scrollview2, and scrollview3) also.
I check there is a method called scrollview.scrollto(x,y). Which used to manage the scroll but when i increase scollto(x, y+scrollviews(i).getSpeed()) than it gives me stackOverflow exception.
I am attaching my code please look into this and give me some suggestion how can solve this problem.
My custom scrollView class is:
public class CustomVerticalObserveScroll extends ScrollView {
private GestureDetector mGestureDetector;
View.OnTouchListener mGestureListener;
public CustomVerticalObserveScroll(Context context, AttributeSet attrs) {
super(context, attrs);
mGestureDetector = new GestureDetector(context, new YScrollDetector());
setFadingEdgeLength(0);
// TODO Auto-generated constructor stub
}
private CustomScrollLisner scrollViewListener = null;
public CustomVerticalObserveScroll(Context context) {
super(context);
mGestureDetector = new GestureDetector(context, new YScrollDetector());
setFadingEdgeLength(0);
}
public CustomVerticalObserveScroll(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
mGestureDetector = new GestureDetector(context, new YScrollDetector());
setFadingEdgeLength(0);
}
public void setScrollViewListener(CustomScrollLisner scrollViewListener) {
this.scrollViewListener = scrollViewListener;
}
#Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
if (scrollViewListener != null) {
scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return super.onInterceptTouchEvent(ev)
&& mGestureDetector.onTouchEvent(ev);
}
// Return false if we're scrolling in the x direction
class YScrollDetector extends SimpleOnGestureListener {
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
if (Math.abs(distanceY) > Math.abs(distanceX)) {
return true;
}
if (Math.abs(distanceX) > Math.abs(distanceY)) {
return true;
}
return false;
}
}
}
and this code I am using to make scroll dependent scroll views.
public class RelativePanoFeature implements IFeaturetype, OnTouchListener {
private String type;
private FeatureCordinates locationCordinates;
private int mOrientation;
private String image;
private FeatureCordinates triggerCordinates;
private String scrollDirection;
private String scrollSpeed;
private String scrollHandler;
CustomVerticalObserveScroll vertical_scroll;
CustomHorizontalObserveScroll horizontal_scroll;
RelativeLayout vsChild;
long timeonDown, timeonUp;
Thread t;
Handler mhandler;
float downx, downy;
int touchId;
public static int scrollid = 0;
public static ArrayList<RelativePanoHandler> storePanoHandler = new ArrayList<RelativePanoHandler>();
public RelativePanoFeature(String type) {
this.type = type;
}
#Override
public void setType(String type) {
this.type = type;
}
#Override
public String getType() {
return type;
}
public void setImage(String image) {
this.image = image;
}
public String getImage() {
return image;
}
public FeatureCordinates getLocation() {
return locationCordinates;
}
public void setLocation(FeatureCordinates featureCordinates) {
this.locationCordinates = featureCordinates;
}
public void setOrientation(int mOrientation) {
this.mOrientation = mOrientation;
}
public int getOrientation() {
return mOrientation;
}
public FeatureCordinates getTrigger() {
return triggerCordinates;
}
public void setTrigger(FeatureCordinates trigger) {
this.triggerCordinates = trigger;
}
public void setScrollDirection(String scrollDirection) {
this.scrollDirection = scrollDirection;
}
public String getScrollDirection() {
return scrollDirection;
}
public void setScrollSpeed(String scrollSpeed) {
this.scrollSpeed = scrollSpeed;
}
public String getScrollSpeed() {
return scrollSpeed;
}
public void setScrollHandler(String scrollHandler) {
this.scrollHandler = scrollHandler;
}
public String getScrollHandler() {
return scrollHandler;
}
public void setTouchId(int touchid) {
this.touchId = touchid;
}
public int getTOuchId() {
return touchId;
}
/* function to draw relative pano */
public void drawRelativePano(final Context con,
final RelativeLayout parent, final Handler handle) {
/* splitting the path from images key in the string */
RelativePanoHandler panHandler = new RelativePanoHandler();
final int height;
final int width;
vsChild = new RelativeLayout(con);
mhandler = handle;
/* giving size of of vertical scroll's child */
LayoutParams imageViewLayoutParams = new LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
vsChild.setLayoutParams(imageViewLayoutParams);
/* splitting the path from images key in the string */
String path[] = getImage().split("images");
try {
/* Initialise loader to load the image inside child */
BackgroundImageLoader loader = new BackgroundImageLoader(vsChild,
Property.FILEPATH + path[1], con);
try {
loader.execute();
} catch (IllegalStateException e) {
e.printStackTrace();
}
/* getting height and width of image from loader object */
height = loader.get().getHeight();
width = loader.get().getWidth();
/*
* condition for putting the child view in vertical scroll and
* implementing the multi directional scroll for event pano
*/
int locWidth = getLocation().getWidth(), locHeight = getLocation()
.getHeight();
System.out.println("Width= " + width + " Location width= "
+ locWidth);
System.out.println("Heoght= " + height + " Location Height= "
+ locHeight
);
if (width > (getLocation().getWidth())
|| height > (getLocation().getHeight())) {
vertical_scroll = new CustomVerticalObserveScroll(con);
horizontal_scroll = new CustomHorizontalObserveScroll(con);
vertical_scroll.setFillViewport(true);
horizontal_scroll.setFillViewport(true);
vertical_scroll.setId(scrollid);
horizontal_scroll.setFadingEdgeLength(0);
/*
* adding the soft later on vertical and horizontal scroll if
* the detected device is on api level 10 or more than that
*/
if (Build.VERSION.SDK_INT > 10) {
vertical_scroll
.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
horizontal_scroll.setLayerType(View.LAYER_TYPE_SOFTWARE,
null);
}
vsChild.setEnabled(true);
/*
* parameters for setting the height and width of vertical
* scroll
*/
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
getLocation().getWidth(), getLocation().getHeight());
params.leftMargin = getLocation().getX();
params.topMargin = getLocation().getY();
vertical_scroll.setLayoutParams(params);
/* adding vertical scroll child this child will hold the image */
vertical_scroll.addView(vsChild, width, height);
horizontal_scroll.setLayoutParams(params);
/*
* adding vertical scroll as a child of horizontal scroll for
* multidirectional scrolling
*/
horizontal_scroll.addView(vertical_scroll);
/*
* at last add this horizontal scroll in side parent which will
* hold the multidirectional scroll
*/
parent.setTag(getScrollHandler());
parent.addView(horizontal_scroll);
// vertical_scroll.setId(id)
panHandler.setVerticalScroll(vertical_scroll);
panHandler.setHandlerTag(getScrollHandler());
panHandler.setPanoSpeed(Integer.parseInt(getScrollSpeed()));
storePanoHandler.add(panHandler);
int size = storePanoHandler.size();
System.out.println("Vertical Scroll objec size=" + size);
}
System.out.println("TAg= " + parent.getTag());
String scdir = getScrollDirection();
System.out.println("Scroll Directoion= " + scdir);
scrollid++;
if (getScrollDirection().equalsIgnoreCase("Y")) {
vertical_scroll.setScrollViewListener(new CustomScrollLisner() {
#Override
public void onScrollChanged(
CustomHorizontalObserveScroll scrollView, int x,
int y, int oldx, int oldy) {
}
#Override
public void onScrollChanged(
CustomVerticalObserveScroll scrollView, int x,
int y, int oldx, int oldy) {
if (scrollView == storePanoHandler.get(getTOuchId())
.getVerticalScroll()) {
for (int i = 0; i < storePanoHandler.size(); i++) {
storePanoHandler.get(i).getVerticalScroll()
.scrollTo(x, y);
storePanoHandler.get(i).getVerticalScroll().
// storePanoHandler.
// .get(i)
// .getVerticalScroll()
// .scrollTo(
// x,
// oldy
// + storePanoHandler.get(
// i)
// .getPanoSpeed());
}
}
}
});
}
// if (getScrollDirection().equalsIgnoreCase("X")) {
// vertical_scroll.setScrollViewListener(new CustomScrollLisner() {
//
// #Override
// public void onScrollChanged(
// CustomHorizontalObserveScroll scrollView, int x,
// int y, int oldx, int oldy) {
// // if (scrollView == storePanoHandler.get(getTOuchId())
// // .getVerticalScroll()) {
// //
// // for (int i = 0; i < storePanoHandler.size(); i++) {
// // storePanoHandler.get(i).getVerticalScroll()
// // .smoothScrollTo(x, y);
// //
// // }
// // }
// }
//
// #Override
// public void onScrollChanged(
// CustomVerticalObserveScroll scrollView, int x,
// int y, int oldx, int oldy) {
// }
// });
// }
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
/*
* set touch listeners on vertical and horizontal scrolls it will use to
* disable the scroll for it's parent like [image or view pager when
* user interacting with any of custom scroll]
*/
horizontal_scroll.setOnTouchListener(this);
vertical_scroll.setOnTouchListener(this);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
boolean scrollingPanaroma = true;
// changing token value for getting scroll
if (v == vertical_scroll || v == horizontal_scroll) {
/*
* Disabling the parent control [list, pager] when user interacting
* with multidirectional scroll
*/
System.out.println("Pano touch Id= " + vertical_scroll.getId());
setTouchId(vertical_scroll.getId());
if (scrollingPanaroma == true) {
v.getParent().getParent().getParent()
.requestDisallowInterceptTouchEvent(true);
}
/*
* enable the parent control [list, pager] when user done with
* multidirectional scroll
*/
else {
v.getParent().getParent().getParent()
.requestDisallowInterceptTouchEvent(false);
}
}
return false;
}
}
Please help me to solve this out because I am really stucked at this point. Thnaks.
Here is the code I used to slow down the scroll speed of ScrollView programmatically,
ObjectAnimator anim = ObjectAnimator.ofInt(mScrollView, "scrollY", mScrollView.getBottom());
anim.setDuration(9000);
anim.start();
mScrollView - Your ScrollView
mScrollView = (ScrollView) findViewById(R.id.scrollView1);
anima.setDuration(int Value) - greater the value, slower the scroll
I used the code block in Switch Button OnCheckedChangedListener.

ScrollView stops updating Items when reaching its bounds

So the problem I am facing right now is the following, I want to create a fisheye view (so a view which contains items while the item in the middle is bigger then the other ones like you will see in the MAC OS itembar or somewhere else).
So far I have extended the HorizontalScrollView to achieve that and after some testing everything seemed to work fine, so when moving the scrollview the items are updated properly depending on their position.
However there is one problem that occurs if the scrollview "bounces" against its borders. So if the ScrollView moves to fast the "getScrollX()" will give me a value that is smaller 0 or bigger then the max bounds. After that happens the items are no longer resized, which is quite strange.
I checked my code and the resize methods of my items are called, yet i don´t know why the items are no longer updated.
The ScrollView class looks like the following
public class HorizontalFishEyeView extends HorizontalScrollView
{
//****************************************************************************************************
// enums
//****************************************************************************************************
public enum ONTOUCHEND
{
//-----undefined value
NONE,
//-----meaning to keep scrolling after the touch event ended
SCROLL_ON_END,
//-----meaning to continue to the next base after the touch event ended
CONTINUE_ON_END,
//-----meaning to switch the element after the on touch event ended
CHANGE_ELEMENT_ON_END
}
public enum MODE
{
//-----none mode meaning the view is not scrolling or doing the finish animation, thus being idle
NONE,
//-----scroll meaning the view is scrolling after an accelleration
SCROLL,
//-----finish meaning the view is doing the finish animation to move to the actual element
FINISH,
}
//****************************************************************************************************
// variables
//****************************************************************************************************
//-----determines if the view will continue when the finish animation is played
private boolean m_bContinueOnClick = false;
//-----time for the scroll animation
private long m_nScrollAnimationTime = 0;
//------the multiplier to be used for the velocity on initaial start
private float m_fVelocityMultiplier = 1.0f;
//-----the direction of the velocity however the reverse value to use it in conjunction with the decrement
private int m_nVelocityDirectionReverse = 0;
//------the velocity provided when the event has ended
private float m_fVelocity = 0.0f;
//-----determines hwo much the velocity decreases per millisecond
private float m_fVelocityDecrement = 0.001f;
//-----time when the touch event was started
private long m_nStartTime = 0;
//-----the x position of the touch event
private float m_nXPosition = -1.0f;
//-----determines when the animation for moving shall be canceled
private final float m_fVelocityThreshold = 0.25f;
//-----determines the time, e.g the start time of the animation and stores the time each time the draw method is called
//-----while the finish animation is in progress
private long m_nFinishAnimationTime = 0;
//-----determines how much pixel the layout will be moved for each millisecond passed,
//-----while the finish animation is playing
private double m_dFinishAnimationIncrements = 0.0;
//-----the actually duration of the finish animation, this value is dependent of the difference distance
//-----which the view has to be moved, so at max this will bei 0.5 times m_nFinishAnimationTime
private int m_nFinishAnimationDuration = 0;
//-----determines the distance which the view has to be moved in order to set the selected element into focus
private int m_nFinishRemainingDiff = 0;
//-----the position which the view will have as its left margin,
//-----this value us determined when the user lets go of the view
private int m_nFinishTargetPosition = 0;
//-----the animation time the finish animation when the user lets go of the view
private int m_nAnimationTime = 0;
//-----the position of the element which is closest to the selector, thus it actually is the selected element
private FishEyeItem m_nClosestElement = null;
//-----scalefactor used to calculate the min item size, thus m_nItemSizeMin = nItemSize * m_fItemSizeMaxScale
private float m_fItemSizeMinScale = -1;
//-----the size of the image of the item when not selected
private int m_nItemSizeMin = -1;
//-----scalefactor used to calculate the max item size, thus m_nItemSizeMax = nItemSize * m_fItemSizeMaxScale
private float m_fItemSizeMaxScale = -1;
//-----the size of the image of the item when selected
private int m_nItemSizeMax = -1;
//-----the difference in item size between the max and the min value
private int m_nItemSizeDiff = -1;
//-----determines at which distance the item size will always be min
private int m_nMaxDiff = 0;
//-----the middel point of the view, used to determine the distance of an item and thus its size
private int m_nReferenceX = 0;
//-----event listener attached to this view
protected AnimationEventListener m_oEventListener;
//-----this factor is multiplied by the velocity up on the UP event to determine the remaining scroll
private float m_fVelocityScaleFactor = 0.25f;
//-----the mode in which the fisheyeview currently is
private MODE m_eMode = MODE.NONE;
//-----the reference to the one and only child in the scrollview, as it should be
private LinearLayout m_oChild = null;
//-----number of items whose bitmap will still be available even if they are not visible
private int m_nItemBuffer = 2;
//-----activity to use
private Activity m_oActivity = null;
//-----scalefactor to use
private float m_fScaleFactor = 1.0f;
//-----determines if the itemsize is stable thus each item is the same size, used to prevent unnecessary calculations
private boolean m_bItemSizeStable = false;
//****************************************************************************************************
// constructor
//****************************************************************************************************
public HorizontalFishEyeView(Context context)
{
super(context);
}
public HorizontalFishEyeView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
//****************************************************************************************************
// public
//****************************************************************************************************
/**
* this method will set up the view before it is used, thus it needs to be called before
* #param oActivity
* #param fItemSizeMinScale
* #param fItemSizeMaxScale
* #param fScaleFactor
* #param bItemSizeStable
*/
public void Initialize(Activity oActivity, float fItemSizeMinScale, float fItemSizeMaxScale, float fScaleFactor, boolean bItemSizeStable)
{
try
{
m_oActivity = oActivity;
m_nReferenceX = (int)(getWidth()*0.5f);
m_fItemSizeMaxScale = Math.min(1.0f, Math.max(0.0f, fItemSizeMaxScale));
m_fItemSizeMinScale = Math.min(1.0f, Math.max(0.0f, fItemSizeMinScale));
m_bItemSizeStable = bItemSizeStable;
m_fScaleFactor = fScaleFactor;
}
catch(Exception e)
{
Log.d("Initialize", e.toString());
}
}
public void Clear()
{
try
{
if(m_oChild!=null)
{
for(int i=0;i<m_oChild.getChildCount();i++)
{
View oChild = m_oChild.getChildAt(i);
if(oChild instanceof FishEyeItem)
{
NetcoMethods.RecycleImageView(((FishEyeItem)oChild).GetImageView());
}
}
m_oChild.removeAllViews();
}
}
catch(Exception e)
{
Log.d("Clear", e.toString());
}
}
public void AddItem(FishEyeItem oItem, LinearLayout.LayoutParams oParams)
{
try
{
if(m_oChild!=null)
{
m_oChild.addView(oItem, oParams);
}
}
catch(Exception e)
{
Log.d("AddItem", e.toString());
}
}
public MODE GetMode()
{
return m_eMode;
}
public void Reinitialize()
{
}
public void Deinitialize()
{
}
/**
* adds an animation listener to the list
* #param listener
*/
public void SetAnimationEventListener(AnimationEventListener listener)
{
m_oEventListener = listener;
}
public void ScrollTo()
{
try
{
}
catch(Exception e)
{
Log.d("ScrollTo", e.toString());
}
}
public LinearLayout GetChild()
{
return m_oChild;
}
//****************************************************************************************************
// private
//****************************************************************************************************
/**called when the size was calculated*/
private void SizeCalculated(Object o)
{
try
{
if(m_oEventListener!=null)
{
m_oEventListener.AnimationEvent(o);
}
}
catch(Exception e)
{
Log.d("AnimationEndEvent", e.toString());
}
}
/**
* calculates the sizes for an item, if m_bItemSizeStable is set to true this will only be done once
* #param nItemSize, the size of the item which will be used
*/
private void CalulateItemSize(int nItemSize)
{
try
{
if(!m_bItemSizeStable)
{
m_nItemSizeMax = (int)(nItemSize * m_fItemSizeMaxScale);
m_nItemSizeMin = (int)(nItemSize * m_fItemSizeMinScale);
m_nItemSizeDiff = m_nItemSizeMax - m_nItemSizeMin;
m_nMaxDiff = nItemSize*2;
}
else if(m_nItemSizeMax==-1)
{
m_nItemSizeMax = (int)(nItemSize * m_fItemSizeMaxScale);
m_nItemSizeMin = (int)(nItemSize * m_fItemSizeMinScale);
m_nItemSizeDiff = m_nItemSizeMax - m_nItemSizeMin;
m_nMaxDiff = nItemSize*2;
}
}
catch(Exception e)
{
Log.d("CalculateItemSize", e.toString());
}
}
/**
* this method will Resize and item in the view depending on its position
* #param oItem the item which shall be resized
* #param nDiff the distance of the item from the middle of he view
* #param nCurrentClosestDiff the currently know closest distance, if the item is closer the given nDiff will be used
*/
private void DeterminenSize(FishEyeItem oItem, int nDiff, int nCurrentClosestDiff)
{
try
{
if(oItem!=null)
{
CalulateItemSize(oItem.getWidth());
//-----check if the item can be resized
if(oItem.GetCanBeResized())
{
//System.out.println("Item is "+ oItem.GetImagePath());
//System.out.println("Item Diff is "+ nDiff);
//-----items is in range
if(nDiff<m_nMaxDiff)
{
//-----determine whether this element is closer to the selector then the previously known
if(nCurrentClosestDiff==-1)
{
nCurrentClosestDiff = nDiff;
m_nClosestElement = oItem;
SizeCalculated(m_nClosestElement);
}
else
{
if(nDiff<nCurrentClosestDiff)
{
nCurrentClosestDiff = nDiff;
m_nClosestElement = oItem;
SizeCalculated(m_nClosestElement);
}
}
//-----get the new size
float fRelative = 1.0f - (float)nDiff/(float)m_nMaxDiff;
int nNewItemSize = m_nItemSizeMin + (int)(fRelative * m_nItemSizeDiff);
//-----set the new size
oItem.Resize(nNewItemSize, nNewItemSize);
oItem.SetIsInRange(true);
}
else
{
//----if the item is now out of range set it to the minimum size
if(oItem.GetIsInRange())
{
//-----set the minimum size
oItem.Resize(m_nItemSizeMin, m_nItemSizeMin);
oItem.SetIsInRange(false);
}
}
}
}
}
catch(Exception e)
{
Log.d("DeterminenSize", e.toString());
}
}
//****************************************************************************************************
// overrides
//****************************************************************************************************
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt)
{
super.onScrollChanged(l, t, oldl, oldt);
try
{
if(m_eMode == MODE.FINISH)
{
}
else
{
//------get the top element which must be a linear layout
if(m_oChild!=null)
{
m_oChild.setWillNotDraw(false);
FishEyeItem oFishEyeItem = null;
View oChildView = null;
ImageView oImage = null;
String cFilename = null;
int nPositionStart = 0;
int nPositionEnd = 0;
int nItemSize = 0;
int nScroll = getScrollX();
int nBoundEnd = getWidth();
int nItemPosition = 0;
int nCurrentClosestDiff = -1;
System.out.println(nScroll);
for(int i=0;i<m_oChild.getChildCount();i++)
{
oChildView = m_oChild.getChildAt(i);
//-----check if the child is of a certain type
if(oChildView instanceof FishEyeItem)
{
oFishEyeItem = (FishEyeItem)oChildView;
nItemSize = oFishEyeItem.getWidth();
nPositionStart = i * nItemSize;
nPositionEnd = nPositionStart + nItemSize;
oImage = oFishEyeItem.GetImageView();
cFilename = oFishEyeItem.GetImagePath();
//-----check if the item is in visible area
if(oImage!=null)
{
//-----image is in visible area
if(nPositionEnd>=nScroll - (m_nItemBuffer * nItemSize) && nPositionStart - (m_nItemBuffer * nScroll)<=nBoundEnd)
{
//-----check if image needs to be loaded
if(!oFishEyeItem.GetIsImageLoaded())
{
oFishEyeItem.SetIsImageLoaded(true);
new DownloadTaskImage(m_oActivity,
oImage,
cFilename,
nItemSize,
nItemSize,
m_fScaleFactor,
POWERROUNDMODES.ROUND).execute((Void)null);
}
//-----get the item position in the fisheyeview
nItemPosition = nPositionStart - nScroll + (int)(nItemSize*0.5f);
DeterminenSize(oFishEyeItem, Math.abs(m_nReferenceX - nItemPosition), nCurrentClosestDiff);
}
else
{
//-----check if an image can be recycle
if(oFishEyeItem.GetIsImageLoaded())
{
oFishEyeItem.SetIsImageLoaded(false);
new RecycleTaskImage(oImage).execute((Void)null);
}
}
}
}
}
}
}
}
catch(Exception e)
{
Log.d("onScrollChanged", e.toString());
}
}
#Override
public boolean onTouchEvent(MotionEvent oEvent)
{
super.onTouchEvent(oEvent);
try
{
switch(oEvent.getAction())
{
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_MOVE:
break;
default:
break;
}
}
catch(Exception e)
{
Log.d("onTouchEvent", e.toString());
}
return true;
}
protected void onFinishInflate()
{
super.onFinishInflate();
try
{
m_oChild = (LinearLayout)getChildAt(0);
}
catch(Exception e)
{
Log.d("onFinishInflate", e.toString());
}
}
}
Don´t mind some of the unused variables as they are intended to be used later to realize an autoscroll feature once the view itself has finished scrolling (so that the currently closed item will always be in the middle up on letting go of the scrollview).
The View requires to actually be filled with "FishEyeItem"s, which are then used to load images and resize the content. Those Items are loaded during runtime after I have gained the list of items that i need to display.
The FishEyeItem code is the following.
public class FishEyeItem extends RelativeLayout
{
//****************************************************************************************************
// variables
//****************************************************************************************************
//-----determines if this item can be resized
private boolean m_bCanBeResized = false;
//-----path to the image of this fisheye items image
private String m_cImagePath = null;
//-----determines if this item is in range for the fisheye calculation
private boolean m_bIsInRange = true;
//-----determines if the image is loaded already, thus occupying memory
private boolean m_bIsImageLoaded = false;
//-----id of the image4view holding the image
private int m_nImageViewID = -1;
//-----the id of the view in this view which is responsible for resizing
private int m_nResizeViewID = -1;
//****************************************************************************************************
// constructor
//****************************************************************************************************
public FishEyeItem(Context context)
{
super(context);
}
public FishEyeItem(Context context, AttributeSet attrs)
{
super(context, attrs);
}
//****************************************************************************************************
// setter
//****************************************************************************************************
public void SetCanBeResized(boolean bValue)
{
m_bCanBeResized = bValue;
}
public void SetImagePath(String cValue)
{
m_cImagePath = cValue;
}
public void SetIsInRange(boolean bValue)
{
m_bIsInRange = bValue;
}
public void SetIsImageLoaded(boolean bValue)
{
m_bIsImageLoaded = bValue;
}
public void SetImageViewID(int nValue)
{
m_nImageViewID = nValue;
}
public void SetResizeViewID(int nValue)
{
m_nResizeViewID = nValue;
}
//****************************************************************************************************
// getter
//****************************************************************************************************
public boolean GetCanBeResized()
{
return m_bCanBeResized;
}
public String GetImagePath()
{
return m_cImagePath;
}
public boolean GetIsInRange()
{
return m_bIsInRange;
}
public boolean GetIsImageLoaded()
{
return m_bIsImageLoaded;
}
public int GetImageViewID()
{
return m_nImageViewID;
}
public int GetResizeViewID()
{
return m_nResizeViewID;
}
public ImageView GetImageView()
{
ImageView oView = null;
try
{
oView = (ImageView)findViewById(m_nImageViewID);
}
catch(Exception e)
{
Log.d("GetImageView", e.toString());
}
return oView;
}
//****************************************************************************************************
// getter
//****************************************************************************************************
public void Resize(int nWidth, int nHeight)
{
try
{
View oView = findViewById(m_nResizeViewID);
if(oView!=null)
{
System.out.println("Resizing Item" + m_cImagePath);
//-----set the minimum size
RelativeLayout.LayoutParams oParams = (RelativeLayout.LayoutParams)oView.getLayoutParams();
oParams.width = nWidth;
oParams.height = nHeight;
oView.setLayoutParams(oParams);
}
}
catch(Exception e)
{
Log.d("Resize", e.toString());
}
}
}
So essentially everytime the onScrollChanged() is called, the image of an item will be loaded or recycled (both are in running in a async task so they dont block the scrolling and GUI). Also the size of an item will be determined if it is a certain distance away from the middle of the scrollview.
Like I said the Resize() method always gets called (thats why the system.out is there) but when "bouncing" against the borders the items are no longer resized.
So I am guessing the problem is somewhere in the HorizontalScrollView class itself, e.g. a certain flag gets set when "bouncing" against the borders.
EDIT:
So okay I was able to prevent that the items were not able to be updated anymore by simply checking the getScrollX() in the onscrollchanged() and returning if that value is <= 0 or if the value is >= the max bounds. However this still does not explain the fact that the items were not updated anymore when "bouncing" against the borders.

Categories

Resources