I'm trying to create a simple Android Launcher.
I'm also using a live wallpaper (Kustom LWP) that I can tap certain areas of to perform actions.
I'm able to see the wallpaper using these style properties:
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowShowWallpaper">true</item>
<item name="android:windowBackground">#android:color/transparent</item>
<item name="android:windowContentOverlay">#null</item>
But if I try to tap an item on my wallpaper, the touch doesn't pass through.
I've tried android:clickable="false and android:focusable="false", and setting all of my views to have a setOnTouchListener that returns false, but none of these fix the issue.
How would I go about doing this?
Answering my own question: after doing some digging in Launcher3, I found that I needed to use WallpaperManager's sendWallpaperCommand function on the touch listener for your homescreen view:
import android.app.WallpaperManager;
private final int[] mTempXY = new int[2];
WallpaperManager mWallpaperManager = WallpaperManager.getInstance(context);
...
homeScreenView.setOnTouchListener((v, event) -> {
onWallpaperTap(v,event);
return true;
});
...
protected void onWallpaperTap(View view, MotionEvent ev) {
final int[] position = mTempXY;
view.getLocationOnScreen(position);
int pointerIndex = ev.getActionIndex();
position[0] += (int) ev.getX(pointerIndex);
position[1] += (int) ev.getY(pointerIndex);
MainActivity.mWallpaperManager.sendWallpaperCommand(view.getWindowToken(),
ev.getAction() == MotionEvent.ACTION_UP
? WallpaperManager.COMMAND_TAP : WallpaperManager.COMMAND_SECONDARY_TAP,
position[0], position[1], 0, null);
}
And now my wallpaper can receive touch input.
Related
I use Android studio and I have this image with a transparent background. Whenever i click on it it'll bring me to another Activity. But even when I click on the transparent part of the image it'll bring me to the other Activity.
Is it possible to make the nontransparent part clickable (or touchable) and the transparent part unclickable?
Yes this is possible but it becomes much more difficult than just adding an OnClickListener.
The trick is to use a Touch listener instead of click and on either a DOWN or UP event take the position and then either use some simple maths to work out whether it was a transparent area (if the design is a simple one) or, do some more complicated stuff to work out your pixel values at the centre.
new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
If (event.getAction() == MotionEvent.ACTION_DOWN) {
final int x = (int) event.getX();
final int y = (int) event.getY();
//now map the coords we got to the
//bitmap (because of scaling)
ImageView imageView = ((ImageView)v);
Bitmap bitmap =((BitmapDrawable)imageView.getDrawable()).getBitmap();
int pixel = bitmap.getPixel(x,y);
//now check alpha for transparency
int alpha = Color.alpha(pixel);
If (alpha != 0) {
//do whatever you would have done for your click event here
}
}
return true; //we've handled the event
}
}
This is original images and combine from 29 pieces of other small images and final result like this.
For example, when I click the particular area,the small piece will change to another colour.
Is there any solution that I can detect the particular area touch and know which piece of image should change?
set implements OnTouchListener for your class add the unimplement method then put all your image view in array and set setOnTouchListener
ImageView[] image = new ImageView[9];//for exapmle 9
image[i].setOnTouchListener(this);
#Override
public boolean onTouch(View v, MotionEvent event) {
boolean eventConsumed = true;
int x = (int)event.getX();
int y = (int)event.getY();
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
for (int i=0;i<=8;i++)//change the loop size to image array size
{
if (v == image[i]) {
point=i;
// here i'm getting which image i have touched
System.out.println(" image"+i+i+i+i+i+i+i+i+i+i+"is clicked");
}
}else if (action == MotionEvent.ACTION_UP) {
//here wright the condition for which image what color have to give
}
If you only want to show the touched part by changing its color then maybe you should use a selector for each image.
selector.xml -> drawable folder.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/colored_image"
android:state_pressed="true"/>
<item android:drawable="#drawable/normal_image" >
</selector>
ImageView:
<ImageView
android:id="#+id/change_city_small_down_ImageView"
android:clickable="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/changeCityRelativeLayout"
android:layout_marginLeft="5dp"
android:src="#drawable/selector"
</ImageView>
It works, I just used it by overlapping imageviews:
I've been trying to look for a solution, but it seems like no one tried it, or maybe its too simple that I did not find it.
Anyways, I'm trying to achieve a button press with an ImageView. What I want is, ImageView to display a default image, and if pressed (OnTouch) [which has a MotionEvent.ACTION_DOWN block] to run a frame animation. When you release the touch [which has a MotionEvent.ACTION_UP block], it should stop the animation and return to the default image.
Note: Frame animation is continuous repeating, and should not include the default image as one of the looped through image. (Button up image should not be displayed when touching)
Now the problem is, I have the animation working, but as per the android documentation, the first item in the <animation-list> tag will be displayed by default. But if I add the default image (button un-touched state) as the first item, it will be displayed in the loop. Also, when I release the touch, I use stop() method of the AnimationDrawable, which stops the animation at the current frame (image) and I can't seem to find any way to stop and go to default image state.
Here is my code:
button_anim.xml
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false" >
<item android:drawable="#drawable/button1_press1" android:duration="200" />
<item android:drawable="#drawable/button1_press2" android:duration="200" />
<item android:drawable="#drawable/button1_press3" android:duration="200" />
</animation-list>
MainActivity.java
protected void onCreate(Bundle savedInstanceState) {
button = (ImageView)findViewById(R.id.imageView1);
button.setBackgroundResource(R.drawable.button_anim);
buttonAnim = (AnimationDrawable)button.getBackground();
button.setOnTouchListener(buttonTest);
}
private OnTouchListener buttonTest = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
buttonAnim.start();
// Log.d("Test", "Touch down");
} else if (action == MotionEvent.ACTION_UP) {
buttonAnim.stop();
// Log.d("Test", "Touch Stop");
}
return true;
}
};
Default image:- button1_inactive.png
buttonAnim.stop();
buttonAnim.reset();
// Log.d("Test", "Touch Stop");
AnimationDrawable mFrameAnimation = (AnimationDrawable)button.getBackground();
mFrameAnimation.stop();
mFrameAnimation.setVisible(true, true);
I have several Views on an Activity which a user wants to touch quickly in succession and I capture these touches using a TouchListener and handling MotionEvent.ACTION_DOWN. However, if the user is using two hands, it's very likely that the next View will be 'Touch'ed before the user pulls the previous finger up. In this scenario, a MotionEvent.ACTION_MOVE is fired for the first view rather than the desired MotionEvent.ACTION_DOWN for the second view.
Is there any way to work around or prevent this behavior? I've tried dispatching a new event with MotionEvent.ACTION_UP and also removing the event listener but neither seem to work.
The easiest way I found to force single touch across an entire app is to set it using a theme:
<style name="MyTheme" parent="#android:style/Theme.Holo.Light">
<item name="android:windowEnableSplitTouch">false</item>
<item name="android:splitMotionEvents">false</item>
</style>
Manifest:
<application
android:label="#string/app_name"
android:theme="#style/MyTheme" >
if someone still searching for best solution,
put in the xml the following code :
android:splitMotionEvents = false
I did it like this :
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
if(event.getPointerCount() > 1) {
System.out.println("Multitouch detected!");
return true;
}
else
return super.onTouchEvent(event);
}
I think you can override onTouchEvent for any view.
The best way to work around this scenario is to use a ViewGroup and implement the onInterceptTouchEvent method to manually route the touch events as you see fit.
This was originally answered here:
Android multitouch! hack anyone?
Code implementing this solution (found in the comments section of the answer to the above question) is found here:
http://pastebin.com/hiE1aTCw
I have solved this using a custom method - which I did not want to do If anyone finds a better way I'd like to hear about it Thanks:
public static void setViewGroupEnebled(ViewGroup view, boolean enabled)
{
int childern = view.getChildCount();
for (int i = 0; i< childern ; i++)
{
View child = view.getChildAt(i);
if (child instanceof ViewGroup)
{
setViewGroupEnebled((ViewGroup) child,enabled);
}
child.setEnabled(enabled);
}
view.setEnabled(enabled);
}
Override dispatchTouchEvent and intercept all touches there, for all multi touches the pointercount is more than one.
if(ev.getPointerCount() > 1)
{
Log.d("Multitouch detected!");
ev.setAction(MotionEvent.ACTION_CANCEL);
}
This cancels the touches and clears the pressed state of the buttons without taking any further action.
For me it worked like this.
I just put this line in all active styles in my app
<item name="android:splitMotionEvents">false</item>
my style for example
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="android:splitMotionEvents">false</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:splitMotionEvents">false</item>
</style>
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
if (currentapiVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) {
horizentalLinearGridItem.setMotionEventSplittingEnabled(false);
}
Here horizentalLinearGridItem is parentLayout view where we insert child views.
In my code its LinearLayout. In this LinearLayout I added child views. But when I clicked two child views simultaneously, both were getting the events. To block that I used the above code.
I got it to work for me by just adding some code in OnTouchEvent method,
boolean action_down_required = false;
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if(event.getPointerCount() > 1){
action_down_required = true;
return true;
}
if(action_down_required){
action = MotionEvent.ACTION_DOWN;
}
switch (action) {
case MotionEvent.ACTION_DOWN:
// your code goes here...
action_down_required = false;
break;
// Other events...
I have an activity with a Dialog theme and I would like to close (finish) this activity when someone touches the screen anywhere outside this activity's window ? How can I do this ?
Just to point out that there is a way to get dialog-like "touch outside to cancel" behaviour from an Activity themed as a dialog, though I've not fully investigated whether it has unwanted side effects.
Within your Activity's onCreate() method, before creating the view, you're going to set two flags on the window: One to make it 'non-modal', to allow views other than your activity's views to receive events. The second is to receive notification that one of those events has taken place, which will send you an ACTION_OUTSDIE move event.
If you set the theme on the activity to the dialog theme, you'll get the behaviour you want.
It looks something like this:
public class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Make us non-modal, so that others can receive touch events.
getWindow().setFlags(LayoutParams.FLAG_NOT_TOUCH_MODAL, LayoutParams.FLAG_NOT_TOUCH_MODAL);
// ...but notify us that it happened.
getWindow().setFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
// Note that flag changes must happen *before* the content view is set.
setContentView(R.layout.my_dialog_view);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// If we've received a touch notification that the user has touched
// outside the app, finish the activity.
if (MotionEvent.ACTION_OUTSIDE == event.getAction()) {
finish();
return true;
}
// Delegate everything else to Activity.
return super.onTouchEvent(event);
}
}
I found an even simpler answer that has worked perfectly for me. If you're using an activity with the dialog theme then you can apply this.setFinishOnTouchOutside(true); to the activity's onCreate() method.
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_yoptions);
this.setFinishOnTouchOutside(true);
}
It's very simple, just set the property canceledOnTouchOutside = true. look at the example:
Dialog dialog = new Dialog(context)
dialog.setCanceledOnTouchOutside(true);
It is possible quite easily:
First define your own theme in style.xml:
<style name="DialogSlideAnim" parent="#android:style/Theme.Holo.Dialog">
<item name="android:windowContentOverlay">#null</item>
<item name="android:windowCloseOnTouchOutside">true</item>
</style>
Then in your manifest apply this theme to activity:
<activity
android:label="#string/app_name"
android:name=".MiniModeActivity"
android:theme="#style/DialogSlideAnim" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
A combination of Gregory and Matt's answers worked best for me (for Honeycomb and presumably others). This way, views outside will not get touch events when the user tries to touch-outside-cancel the dialog.
In the main Activity, create the touch interceptor in onCreate():
touchInterceptor = new FrameLayout(this);
touchInterceptor.setClickable(true); // otherwise clicks will fall through
In onPause() add it:
if (touchInterceptor.getParent() == null) {
rootViewGroup.addView(touchInterceptor);
}
(rootViewGroup may have to be either a FrameLayout or a RelativeLayout. LinearLayout may not work.)
In onResume(), remove it:
rootViewGroup.removeView(touchInterceptor);
Then, for the dialog-themed Activity, use the code Gregory offered (copied here for your convenience):
public class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Make us non-modal, so that others can receive touch events.
getWindow().setFlags(LayoutParams.FLAG_NOT_TOUCH_MODAL, LayoutParams.FLAG_NOT_TOUCH_MODAL);
// ...but notify us that it happened.
getWindow().setFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
// Note that flag changes must happen *before* the content view is set.
setContentView(R.layout.my_dialog_view);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// If we've received a touch notification that the user has touched
// outside the app, finish the activity.
if (MotionEvent.ACTION_OUTSIDE == event.getAction()) {
finish();
return true;
}
// Delegate everything else to Activity.
return super.onTouchEvent(event);
}
}
If using a dialog theme like android:theme="#style/Theme.AppCompat.Dialog" or any other dialog theme.
On API 11 and after we can use
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setFinishOnTouchOutside(false);
}
call this inside onCreate of the activity.
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Rect dialogBounds = new Rect();
getWindow().getDecorView().getHitRect(dialogBounds);
if (!dialogBounds.contains((int) ev.getX(), (int) ev.getY())) {
return true;
}
return super.dispatchTouchEvent(ev);
}
This code is solved my problem.
I couldn't get the top answer here to work on a Samsung tab running 3.1, so I did this:
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
int xmargin = (ViewUtils.getScreenWidth() - Constants.PRODUCT_DIALOG_WIDTH) / 2;
int ymargin = (ViewUtils.getScreenHeight() - Constants.PRODUCT_DIALOG_HEIGHT) / 2;
if (
x < xmargin ||
x > ViewUtils.getScreenWidth() - xmargin ||
y < ymargin ||
y > ViewUtils.getScreenHeight() - ymargin
) {
finish();
return true;
}
return super.onTouchEvent(event);
}
You'll need to replace Constants.PRODUCT_DIALOG_WIDTH and Constants.PRODUCT_DIALOG_HEIGHT with the width/height of your dialog. Mine was used in a number of places so I made them constants.
You'll also need to implement your own method to get the the screen width and height, which you can find easily on this here site. Don't forget to account for the Android header in that!
It's kind of ugly, and I'm not proud, but it works.
You can reference the dialog.java code from android source:
public boolean onTouchEvent(MotionEvent event) {
if (mCancelable && mCanceledOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(event)) {
cancel();
return true;
}
return false;
}
private boolean isOutOfBounds(MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop();
final View decorView = getWindow().getDecorView();
return (x < -slop) || (y < -slop) || (x > (decorView.getWidth()+slop)) || (y > (decorView.getHeight()+slop));
}
Just modify it to :
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(event)) {
finish();
return true;
}
return false;
}
private boolean isOutOfBounds(MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
final int slop = ViewConfiguration.get(this).getScaledWindowTouchSlop();
final View decorView = getWindow().getDecorView();
return (x < -slop) || (y < -slop) || (x > (decorView.getWidth() + slop)) || (y > decorView.getHeight() + slop));
}
can solve your problem.
The only way I got this to work was
alert = new AlertDialog.Builder(this)....
alert.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(final DialogInterface arg0) {
Log.i(APP_NAME, "in OnDismissListener");
// removeDialog(R.layout.dialog3);
alert.dismiss();
finish();
}
i.e. I had to put both dismiss and finish in explicitly, otherwise I ended up with a small white rectangle in the middle of the screen.
An Activity have dispatchTouchEvent use that
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
finish();
return super.dispatchTouchEvent(ev);
}
Just add this item to styles.xml:
<style name="alert_dialog" parent="android:Theme.Dialog">
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowBackground">#color/float_transparent</item>
<item name="android:windowAnimationStyle">#null</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:backgroundDimAmount">0.4</item>
</style>
And in onCreate() and before setContentView:
setTheme(R.style.alert_dialog);
Using method setFinishOnTouchOutside to enable/disable whether outside is touchable or not.
This is working for activity.
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_yoptions);
/* your code here */
// set outside touchable
this.setFinishOnTouchOutside(true);
}
For those who want to not close the Dialog Application if touch is outside the Dialog.
Add this line
this.setFinishOnTouchOutside(false);
It will not close the dialog box
Just use this theme. Activity will be dismissed on touch outside.
<style name="DialogTheme" parent="Theme.MaterialComponents.DayNight.Dialog">
<item name="android:windowIsTranslucent">true</item>
</style>
Kotlin version worked for me
alert.setOnDismissListener(DialogInterface.OnDismissListener() {
it.dismiss()
})
If there's no API support, you should just use a FrameLayout to fill the screen, and manually build a pop-up. Then you can receive focus anywhere on the screen and show/hide views accordingly.