I know how to do this within the activity class, however this doesn't fit my need. Within my class that extends View, once a method is called i would like the screen orientation to be locked. And then once another method is called i would like it to be unlocked.
Is there a way i can do this within my class that extends View?
Thanks.
#Override
public void onConfigurationChanged(Configuration newConfig) {
// TODO Auto-generated method stub
super.onConfigurationChanged(newConfig);
setContentView(this.gameView);
if (this.gameView.isOrientationChange() == false) {
// Stop the screen orientation changing during an event
switch (this.getResources().getConfiguration().orientation) {
case Configuration.ORIENTATION_PORTRAIT:
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
break;
case Configuration.ORIENTATION_LANDSCAPE:
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
break;
}
}
else {
// allow screen rotations
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
}
In the class extending View, there is an orientationChange boolean field. this is changed by method indicating whether the devices orientation can be changed. This worked but the screen size also changes when the orientation is changed. So if there is a lot of code dependent on the size and is timed, then this may cause more problems than solve.
i think what you are looking for, is configChanges see doc -> http://developer.android.com/reference/android/R.attr.html#configChanges
Maybe I've got a method, just write down that:
set layoutParams.screenOrientation value into: ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED or ActivityInfo.SCREEN_ORIENTATION_LOCKED
Then, call addView or updateViewLayout to update this LayoutParams.
Related
I have an ImageView overlay inside of a RelativeLayout and want to prevent any clicks from going through the ImageView to the Buttons etc that are behind it (thus in effect disabling the entire RelativeLayout).
Is the a simpler way of doing this then iterating the RelativeLayout views and setting them to disabled as I currently am doing using this code:
RelativeLayout rlTest = (RelativeLayout ) findViewById(R.id.rlTest);
for (int i = 0; i < rlTest.getChildCount(); i++) {
View view = rlTest.getChildAt(i);
view.setEnabled(true);
}
you can set the image to be
android:clickable="true"
Simply call rlTest.setClickable(false). This will prevent the click to be propagate to the children
There is a much cleaner way
You can use:
android:onClick="preventClicks"
in XML and in your Activity
public void preventClicks(View view) {}
This works with fragments.
Example inside this Activity has multiple fragments overlapping one another, just by adding the XML attribute in the background of your fragment it will still call the Activity.preventClicks and will prevent touches on fragments behind it
The following solution works in the general case:
_container.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// NOTE: This prevents the touches from propagating through the view and incorrectly invoking the button behind it
return true;
}
});
It basically blocks any touches from propagating through the view by marking the touch event as handled.
This works on both UI controls and layout containers (ie: LinearLayout, FrameLayout etc.).
The solution to set "clickable" as false did not work for me for layout containers either in code or in the view XML.
I assume that you are using onClickListeners.
How about using onTouchListener instead of onClickListeners. By doing this you will have a control over how deep down in your hierarchy the touch even can be visible. For example, if you have toch listeners on a relative-layout(RL) and a image-view(IV)(contained in RL), and you assign touchListeners to both. Now if you return true from IV's touch event, the lower down member RL will not receive the touch event. However if you return false from from IV's touch event, the lower down member RL will receive the touch event.
Hope this helps!
Just add these two listeners:
// Set an OnTouchListener to always return true for onTouch events so that a touch
// sequence cannot pass through the item to the item below.
view.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
v.onTouchEvent(event);
return true;
}
});
// Set an OnHoverListener to always return true for onHover events so that focus cannot
// pass through the item to the item below.
view.setOnHoverListener(new OnHoverListener() {
#Override
public boolean onHover(View v, MotionEvent event) {
v.onHoverEvent(event);
return true;
}
});
You could use databindings and consume the clicks like this:
android:onClick="#{() -> true}"
In C#, I use an empty delegate:
objectName.Click += delegate {};
I haven't encountered any problems with it but it does prevent clicks from filtering through to underlying controls.
you can also se the root click listener to null:
// Do not process clicks on other areas of this fragment
binding.root.setOnClickListener(null)
This works 100%.
It doesnt affect other listeners that are already set on the fragment's views.
I'm working on a game and ran into a bit of trouble, I might not be doing this correctly as I'm new to the graphic side of Android.
I have a SurfaceView and an ArrayList of my own Card object which extends View. I override the onDraw method for the Card object, and then in the SurfaceView's onDraw I draw everything. The drawing part works as it should.
I now try to detect when an individual card is touched using the onTouchListener, I set the listener for each card, but it detects the touch as if the view that being touched is the SurfaceView. It's possible that my whole way of thinking of this is wrong, so I'm asking for your advice.
Some code:
public GameSurfaceView(Context context) {
super(context);
GAME_STATE = GameState.LOADING;
this.context = context;
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Canvas c = holder.lockCanvas(null);
onDraw(c);
holder.unlockCanvasAndPost(c);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
});
}
SurfaceView onDraw():
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRGB(30, 180, 30);
for (Card card : user.getTableCards()) {
card.draw(canvas);
}
}
Card's onDraw():
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(getBitmap(), x, y, null);
}
and onTouch
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("TOUCH", "Class: " + v.getClass().toString());
if (v.getClass() == Card.class) {
Log.d("CARD", "Touched: " + ((Card) v).getValue());
}
return true;
}
I'm always getting the SurfaceView class being logged.
Is this even the right way of doing something like that? I need to detect touch on individual objects of the same class..
I currently do this by adding a member of type Rect to each card and then looping over and checking if the x and y of the touch are contained in that rect, but it seems like an expensive way, iterating over each card (even with max of 52) and checking it..
Thanks
You're mixing up two different concepts, I'll explain both in A) and B).
You have two options:
A) If you want to continue using SurfaceView (which might be the right choice for game, since it gives you more control over how things are drawn), your Card object should extend Drawable, not View. Drawable is a dumb class that you just draw to a canvas or pass to widgets as their background etc. Android doesn't need to know much more about it. In this case you will need to check what card was hit yourself, like you described. Android can not handle this for you. You're doing this with a View instead of Drawable, this is wrong! Or at least a huge waste. See below for how to do it properly with views.
B) Use views, which will handle touch events and many other things for you.
View is a complex class that is supposed to exist in android's view hierarchy. The way you're using it you're throwing away most of it's functionality - when you draw a view straight to a canvas android isn't really aware of the view at all, so it can't handle touch events.
A proper view-based implementation of your cards could look like this: Use a viewgroup as your base, this is what you set in setContentView(). This ViewGroup could just be a LinearLayout, or if you want the ability to set an arbitrary position and size on your cards of your cards, you could use AbsoluteView, although that's deprecated.
Your cards are then added to the ViewGroup using addView(). The positioning of your Card view within this is governed by a LayoutParams object that you pass to addView(). This way android is aware of it's existence, and will handle touch events like this:
The touch event first goes to your ViewGroup
The ViewGroup onTouchEvent will send the event down to down to it's children's onTouchEvent (which have been added via addView()). So you just override this method in your Card class. You inherently know which Card's onTouchEvent was called since it gets called on the object itself.
In your Card's onTouchEvent, you also let the parent know that you've handled the touch event by returning true. If you don't do this, the parent can handle the event instead.
To be perfectly clear: You were NOT adding the views to SurfaceView, in fact, you can't since it is a not a ViewGroup. It's just a complex View object that allows you to do arbitrary drawing operations within it. But it is always a leaf of the View hierarchy, and can only handle it's own onTouchEvents, it will not send it down to children because it can't have any.
your are drawing cards on a SurfaceView, it's like drawing image on a wall when you come to touch the image you're basically touching the wall, the image is now a part of the wall you can't separate them.
Here's an idea that may help:
you have a Card which extends View, why don't you just Use a RelativeLayout instead of your SurfaceView and ADD your cards to it (side by side maybe). This way you have a concrete view for each card that you can register an OnClickListener to each and handle your touch event...
I have multiple HorizontalScrollViews inside a ScrollView. Horizontal scroll isn't smooth at all. I have to scroll almost perfectly horizontally for scrolling to work. Is there a simple fix to tweak this ??? Thanks!
You can use Recycler view with Staggered layout manager
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.HORIZONTAL);
RecyclerViewAdapter recyclerViewAdapter = newRecyclerViewAdapter(this);
recyclerView.setAdapter(recyclerViewAdapter); //Don't miss to initialize your adapter
This class creates a ScrollView containing a HorizontalScrollView combined into one class. You can put stuff inside it using the AddChild() method. The dispatchTouchEvent overide keeps the scrolling smooth so you can pan around with a single slide of the finger.
(I recently used this to wrap a programmatically created TextView)
class MultiScrollView extends ScrollView
{
public HorizontalScrollView hscroll;
public MultiScrollView ( Context context )
{
super( context );
}
public void AddChild( View child )
{
hscroll.addView( child );
}
#Override
public boolean dispatchTouchEvent( MotionEvent event )
{
hscroll.dispatchTouchEvent(event);
onTouchEvent(event);
return true;
}
}
If you are using the horizontal scroll view solution from (http://www.dev-smart.com/archives/34) the solution for the cross focus problem between the scroll view and the list view is blocking the focus to the scroll view once you have focus on the list view.
From a technical point of view you should add the following line to the onScroll function inside the HorizontalListView class.
getParent().requestDisallowInterceptTouchEvent(true);
Hope this helps.
I've found the solution and still can't believe that this is what you have to do to make this work normal! Just added blank onClickListener to the each item in the HorizontalScrollView:
item.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
After this slide is really smooth, both upwards and downwards.
In general, you shouldn't be using nested ScrollViews in Android at all, the behaviour of scrolling in this way is unnatural too.
You may want to rethink your layout design, is it anything that couldn't be achieved with an expandable list?
While David's answer works, it has a downside. It passes ScrollView's MotionEvent object directly to HorizontalScrollView.onTouchEvent(), so if HorizontalScrollView or its children try to get the event coordinates, they will get the wrong coordinates which based on ScrollView.
My solution:
public class CustomScrollView extends ScrollView{
/*************skip initialization*************/
#Override
public boolean onInterceptTouchEvent(MotionEvent e){
//returning false means ScrollView is not interested at any events,
//so ScrollView's onTouchEvent() won't be called,
//and all of the events will be passed to ScrollView's child
return false;
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//manually call ScrollView's onTouchEvent(),
//the vertical scrolling happens there.
onTouchEvent(ev);
//dispatch the event,
//ScrollView's child will have every event.
return super.dispatchTouchEvent(ev);
}
}
Just wrap this CustomScrollView around the HorizontalScrollView in your layout file.
I have a LinearLayout, and this LinearLayout will hold dynamically placed views. I need to find out what the width of the children of LinearLayout, however this has to be done in onCreate method. From researching I've found out that you can't use getWidth from this method. So instead I'm using onWindowFocusChanged, which works for the parent LinearLayout (returning me the actual size), but it doesn't work with its children.
Another thing I noticed is that when the screen is fading away and the screen is locked, I can see at the logs the actual width of the children being returned (I think the activity is being paused).
I'm really stuck and this is needed because I need to dynamically place those views depending on the children width.
You might be able to get with the below. But as others pointed out, this probably isn't a great idea.
LinearLayout.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
LinearLayout.getMeasuredWidth();
inside the onCreate , views still can't know the state of the nearby views and the children ,etc... so only after all is prepared and the layout process is done , you can get the size of views .
here's a quick code for getting the size of the view just before it's being drawn:
private static void runJustBeforeBeingDrawn(final View view, final Runnable runnable)
{
final ViewTreeObserver vto = view.getViewTreeObserver();
final OnPreDrawListener preDrawListener = new OnPreDrawListener()
{
#Override
public boolean onPreDraw()
{
Log.d(App.APPLICATION_TAG, CLASS_TAG + "onpredraw");
runnable.run();
final ViewTreeObserver vto = view.getViewTreeObserver();
vto.removeOnPreDrawListener(this);
return true;
}
};
vto.addOnPreDrawListener(preDrawListener);
}
alternatively , you can use addOnGlobalLayoutListener instead of addOnPreDrawListener if you wish.
example of usage :
runJustBeforeBeingDrawn(view,new Runnable()
{
#Override
public void run()
{
int width=view.getWidth();
int height=view.getHeight();
}
});
another approach is to use onWindowFocusChanged (and check that hasFocus==true) , but that's not always the best way ( only use for simple views-creation, not for dynamic creations)
EDIT: Alternative to runJustBeforeBeingDrawn: https://stackoverflow.com/a/28136027/878126
https://stackoverflow.com/a/3594216/1397218
So you should somehow change your logic.
How do you detect that the user has changed the orientation of the display screen from portrait to landscape and vice versa?
The Android application must adjust to the new orientation. The system should be sending a change of orientation message to all open applications so that they can adjust. I could not find anything about this in the docs? Sorry if i copy these statement from somewhere else but i'm kinna new ina android/java, can someone guide me along?
Its just one of the Preference screen in multiple tabs i have, but i just want mere change of screen orientation when the phone position changes...
public class Tab2Activity extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.tab2);
}
}
Android will automatically recreate (destroy and then create) your activity when the orientation changes. If you want a special layout for landscape, you can create a special resource file in the layout-land folder, which will be loaded after the activity is recreated.
On the other hand, if you want to override the default behavior so that you can do something custom when the orientation changes, you can override the onConfigurationChanged method like so:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
//do something if portrait...
}
}
http://developer.android.com/reference/android/app/Activity.html#onConfigurationChanged%28android.content.res.Configuration%29
EDIT: in code (add this to your Activity):
#Override
public void onConfigurationChanged(Configuration _newConfig){
super.onConfigurationChanged(_newConfig);
int height = getWindowManager().getDefaultDisplay().getHeight();
int width = getWindowManager().getDefaultDisplay().getWidth();
if(width > height){
// landscape
}else{
// portrait
}
}