I've been trying to use selectors in slidingmenu for 2 days without managing to make it work.
Here is what I want to do :
I've a menu which have a ListView in it. I want the selector to point at one particular item and move with the item when the listview is scrolled.
So basically the selector is pointing at the right item but when I'm scrolling, the selector don't move on my phone (android 4.0.4) but it work with the emulator (4.1.2). Do you have any idea of why the menu is not invalidating itself when I ask him to do so ?
/*Setting the sliding menu */
setBehindContentView(R.layout.menu);
setSlidingActionBarEnabled(true);
getSlidingMenu().setMode(SlidingMenu.LEFT);
getSlidingMenu().setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
getSlidingMenu().setFadeEnabled(true);
getSlidingMenu().setFadeDegree(0.35f);
getSlidingMenu().setShadowWidth(15);
getSlidingMenu().setShadowDrawable(R.drawable.shadow);
getSlidingMenu().setSelectorEnabled(true);
getSlidingMenu().setSelectorDrawable(R.drawable.selector);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
getSlidingMenu().setBehindWidth((int) (metrics.widthPixels * 0.8));
getSlidingMenu().setSelectedView(null);
/*Creating the content of the sliding menu*/
/*Now we generate the menu below */
maListViewPerso = (ListView) this.findViewById(R.id.listviewperso);
maListViewPerso = SlidingMenuListCreator.getListView(this, (String) this.getTitle(), maListViewPerso, isMissionSelected, isTourneeOpened);
getSlidingMenu().setOnOpenListener(new OnOpenListener() {
#Override
public void onOpen() {
int wantedPosition = 5; // Whatever position you're looking for
int firstPosition = maListViewPerso.getFirstVisiblePosition() - maListViewPerso.getHeaderViewsCount(); int wantedChild = wantedPosition - firstPosition;
if (wantedChild < 0 || wantedChild >= maListViewPerso.getChildCount()) {
} else {
selectedView = maListViewPerso.getChildAt(wantedChild);
}
getSlidingMenu().setSelectedView(selectedView);
getSlidingMenu().invalidate();
}
});
OnTouchListener mOnTouchListener = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
maListViewPerso.onTouchEvent(event);
int wantedPosition = 5; int firstPosition = maListViewPerso.getFirstVisiblePosition() - maListViewPerso.getHeaderViewsCount(); int wantedChild = wantedPosition - firstPosition;
if (wantedChild < 0 || wantedChild >= maListViewPerso.getChildCount()) {
} else {
selectedView = maListViewPerso.getChildAt(wantedChild); }
getSlidingMenu().setSelectedView(selectedView);
getSlidingMenu().getmViewBehind().invalidate();
return true;
}
};
maListViewPerso.setOnTouchListener(mOnTouchListener);
After all I never find the solution.
But :
I switched to a viewpager navigation mode because that what was users preferred (and discovered that, yes, Fragments are awesome)
Google have finally made this navigation pattern "official" by adding it through the support library and drawerlayout and it works very well for me =)
Related
I have a weird issue with my checkboxes in RecyclerView.
When I click once on it everything works perfectly (move and change style) but when I click twice (enough fast) it performs some part of the code (just move it down and uncheck). I want to prevent it but I don't know how.
I tried the solution from here https://stackoverflow.com/a/16514644/10802597 but it makes it even worse (maybe I do it in a wrong way).
Here is my code:
holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int currentPosition = holder.getAdapterPosition();
if(isChecked){
FinalListItem finalItemBefore = finalListItems.get(currentPosition);
FinalListItem finalItemAfter = new FinalListItem(finalItemBefore.getName(), true);
finalListItems.remove(finalItemBefore);
finalListItems.add(finalItemAfter);
recyclerView.scrollToPosition(0);
holder.linearLayout.setBackgroundResource(R.drawable.listitem_green);
notifyItemMoved(currentPosition, getItemCount() - 1);
}
else{
finalListItems.get(currentPosition).setChecked(false);
notifyItemChanged(currentPosition);
}
}
});
You can use this
private long mLastClickTime = 0;
if (SystemClock.elapsedRealtime() - mLastClickTime < 1000) {
return;
}
mLastClickTime = SystemClock.elapsedRealtime();
inside your click and it will prevent another click for 1 sec
but don't use it inside your checkbox method, from what I can see its adapter so set ID for the whole row and put this method on the row click.
I have a paging algorithm for the RecyclerView, which if done Scroll, a scrolling listener triggers and loads more elements. Initially, I assigned a number of elements to load in the request to the server by default that is 20 for my RecyclerView, which has a fixed size (not wrap_content).
I need to know the number of items that can be visible in the width/height of the defined RecyclerView before loading the data, to determine the amount of items to be requested in the initial load, given that with 20 items on some devices is not enough to activate the listener of the scroll and load more elements.
This is without considering the extra properties of the view, such as padding, margin etc ...
The solution can be in Java Android or Xamarin Android (not Forms) C#.
Update:
For you to have a clue, i have tried this and it works for me, only if I call it when the size of RecyclerView is assigned, inside the OnLayoutChange:
public int GetMaxVisibleItemCountFromRecyclerView(RecyclerView recyclerView)
{
if (recyclerView == null) return 0;
int Width = recyclerView.Width;
int Height = recyclerView.Height;
if (Width == 0 || Height == 0)
return 0;
var layoutManager = recyclerView.GetLayoutManager() as GridLayoutManager;
if (layoutManager == null) return 0;
int widthRatio = Width / layoutManager.SpanCount;
int quantity = (Height / widthRatio) * layoutManager.SpanCount;
return quantity;
}
This solution only works for RecyclerViews that use the GridLayoutManager. I have other RecyclerViews with defined sizes that also use the paging algorithm with a LinearLayoutManager.
I need a similar solution, that works with any LayoutManager of the RecyclerView and does not have to be called inside the OnLayoutChange, is this possible?
Instead of trying to replicate the computations that RecyclerView does to compute layout, let the system do the work for you. The following example lets RecyclerView lay out one item and measurements are taken from that. The dummy item is not displayed and is used just for measurement.
The advantage of this method is that we don't have to replicate what RecyclerView does to measure items. All key measurements are taken into account including padding, margins and decorations.
The following sample shows how this can be accomplished for GridLayoutManager and LinearLayoutManager. StaggeredGridLayoutManager and FlexboxLayoutManager are special cases and aren't taken into account here.
Here is a short video showing the results of this demo app showing that just one page of items was loaded.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private List<String> mItems = new ArrayList<>();
private RecyclerView mRecycler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecycler = findViewById(R.id.recyclerView);
// Sample for vertical LinearLayoutManager.
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
// Sample for GridLayoutManager with 4 spans. Each item comsumes 2 spans.
// GridLayoutManager layoutManager = new GridLayoutManager(this, 4);
// layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
// #Override
// public int getSpanSize(int position) {
// return 2;
// }
// });
// Add single dummy item that will be measured but not be displayed.
mItems.add("Dummy item");
RecyclerViewAdapter mAdapter = new RecyclerViewAdapter(mItems);
mRecycler.setLayoutManager(layoutManager);
mRecycler.setAdapter(mAdapter);
// Take measurements in OnPreDraawListener(). This could also be accomplished with
// mRecyclerView.post(new Runnable()...)
mRecycler.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
mRecycler.getViewTreeObserver().removeOnPreDrawListener(this);
// RecyclerView is laid out with single dummy entry. Get how many of this type
// of item can fit into the visible portion of the RecyclerView.
final int nItems = getInitialLoadCount(mRecycler);
Log.d(TAG, "<<<<Items per page=" + nItems);
// Don't need the dummy entry any more.
mItems.clear();
mRecycler.getAdapter().notifyDataSetChanged();
mItems = new ArrayList<>();
// Fake load...
loadInitialItems(nItems);
return false;
}
});
}
// Determine how many items will fill one screen of the RecyclerView. Call with the
// RecyclerView loaded with at least one item for measurement.
private int getInitialLoadCount(RecyclerView recyclerView) {
int itemsToLoad = 0;
RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();
View firstChild = recyclerView.getChildAt(0);
if (lm instanceof LinearLayoutManager) {
Rect bounds = new Rect();
recyclerView.getDecoratedBoundsWithMargins(firstChild, bounds);
if (lm.canScrollVertically()) {
int recyclerHeightForItems = recyclerView.getHeight() - recyclerView.getPaddingTop()
- recyclerView.getPaddingBottom();
itemsToLoad = (recyclerHeightForItems + bounds.height() - 1) / bounds.height();
} else if (lm.canScrollHorizontally()) {
int recyclerWidthForItems = recyclerView.getWidth() - recyclerView.getPaddingLeft()
- recyclerView.getPaddingRight();
itemsToLoad = (recyclerWidthForItems + bounds.width() - 1) / bounds.width();
}
if (lm instanceof GridLayoutManager) {
// Adjust for GridLayoutManager. All items should to be the same number of spans.
GridLayoutManager glm = (GridLayoutManager) lm;
itemsToLoad *= glm.getSpanCount() / glm.getSpanSizeLookup().getSpanSize(0);
}
}
return itemsToLoad;
}
private void loadInitialItems(final int itemCount) {
// Simulate load of nItems...should be on non-UI thread.
new Thread(new Runnable() {
#Override
public void run() {
for (int i = 1; i <= itemCount; i++) {
sleep(250);
mItems.add("Item #" + i);
}
runOnUiThread(new Runnable() {
#Override
public void run() {
mRecycler.swapAdapter(new RecyclerViewAdapter(mItems), true);
}
});
}
}).start();
}
private static final String TAG = "MainActivity";
}
If your RecyclerView and your item width and height are defined in dp, then you should save their dimensions in dimen.xml. Then you can calculate how many items will fit like this:
float recyclerHeight = getResources().getDimension(R.dimen.recycler_height);
float itemHeight = getResources().getDimension(R.dimen.item_height);
int numOfItemsFit = (int) (recyclerHeight / itemHeight);
If your recyclerView isn't defined by dp, but the rest of the views in this layout are you can try to accomplish the same thing by decreasing the other views height from the total view height. You can check the total view height with:
public int getHeight() {
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
return size.y;
}
If you can't do the above you'll need to measure your RecyclerView inside of OnLayoutChange :( . You can do that with: recyclerView.getMeasuredHeight(); and recyclerView.getMeasuredWidth();
Is it possible to check if a ScrollView is scrolled all its way in the top?
I want to check this so I can enable a SwipeRefreshLayout, otherwise keeping it disabled.
With a ListView it could be done like this, but there's no setOnScrollListener for ScrollViews
listView.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
boolean enable = false;
if(listView != null && listView.getChildCount() > 0){
// check if the first item of the list is visible
boolean firstItemVisible = listView.getFirstVisiblePosition() == 0;
// check if the top of the first item is visible
boolean topOfFirstItemVisible = listView.getChildAt(0).getTop() == 0;
// enabling or disabling the refresh layout
enable = firstItemVisible && topOfFirstItemVisible;
}
swipeRefreshLayout.setEnabled(enable);
}
});
This link might be helpful to You. It shows, how to set scroll listener for ScrollView. Then, refer to #antonio answer.
For your case it would be:
mScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
int scrollY = mScrollView.getScrollY(); //for verticalScrollView
if (scrollY == 0)
swipeRefresh.setEnabled(true);
else
swipeRefresh.setEnabled(false);
}
});
You can use the getScrollY() method (from the View class)
In Kotlin:
scrollView.viewTreeObserver.addOnScrollChangedListener {
if (!scrollView.canScrollVertically(1)) {
// Bottom of scroll view.
}
if (!scrollView.canScrollVertically(-1)) {
// Top of scroll view.
}
}
I am designing an android app, and one thing I would like to happen is when the user clicks the page, I would like to find the distance between where they clicked and a TextView element on the page. So far, what I have done is set the TextView ID by using
android:id="#+id/TableLayout01"
I know I also need to use android:onTouch coordinates somehow, but I am not sure how to incorporate this into the coordinates of my TextView
Thanks
You can put a touch listener on the whole activity and calculate the distance like this.
public void onCreate() {
setContentView(R.layout.somelayout);
...
TextView textView = findViewById(R.id.TableLayout01);
View mainView = getWindow().getDecorView().findViewById(android.R.id.content);
mainView.setonTouchListener(new View.onTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
int rawX = event.getRawX();
int rawY = event.getRawY();
int location[] = new int[2];
textView.getLocationOnScreen(location);
int distanceX = location[0] - rawX;
int distanceY = location[1] - rawY;
// Do something with the distance.
return false;
}
}
...
}
My app has an options menu that is available in almost all Activities, which was created by implementing onCreateOptionsMenu(). But in one Activity there is a PopupWindow, and when this PopupWindow has focus (required for proper functioning) tapping the menu button does not bring up the options menu.
PopupWindows do not have an onCreateOptionsMenu() function. Is there some other way to add an options menu to a PopupWindow?
Alternatively, is there a way to get the options menu from the Activity behind it to show up when the user taps the menu button?
I solved this by intercepting the menu key and calling openOptionsMenu() on the activity. Here is the key listener:
OnKeyListener mMenuKeyListener = new OnKeyListener() {
#Override
public boolean onKey(View view, int keyCode, KeyEvent event) {
if(keyCode==KeyEvent.KEYCODE_MENU) {
activity.openOptionsMenu();
return true;
} else {
return false;
}
}
};
I think you have to add this key listener to every view in the PopupWindow to get it to work, so I wrote a function to do that:
public void setupMenuKeyListenerRecursive(View v) {
if (v != null) {
try {
ViewGroup viewGroup = (ViewGroup)v;
int childCount = viewGroup.getChildCount();
for (int index = 0; index < childCount; index++) {
View child = viewGroup.getChildAt(index);
setupMenuKeyListenerRecursive(child);
}
} catch (Exception e) {
}
v.setOnKeyListener(mMenuKeyListener);
}
}