I was trying, with ViewPager2, to get default gallery-like experience of common Android SmartPhone, where you can zoom an image with pan and pinch controls along with the ability to navigate to another photo by swipe gestures.
I faced a problem in which when the zoomed image is swiped expecting it to get panned, instead of that, the ViewPager2 switched to another page.
ViewPager2 responds to the swipe event and causes page change and it doesn’t let zoomable view to respond to that event.
How do I solve the problem? Thanks.
The only solution I found is to use the old ViewPager with which it is possible to intercept events and if the photo is enlarged, it is possible to disable the page change and allow the management of the zoom.
First I defined a field, in a static class, to keep track of the state the photo is in:
public class Utilities {
....
static boolean isZoomed;
private Utilities () { }
static public void setIsZoomed (boolean z) {
isZoomed = z;
}
static public boolean getIsZoomed () {
return isZoomed;
}
....
}
Then in the custom class for managing the zoom I detect the status of the photo:
public class TouchImageView extends ImageView {
...
Utilities.setIsZoomed(normalizedScale != 1);
...
}
Finally, in the ViewPager custom class I disable or allow pagination based on the state of the photo:
public class CustomViewPager extends ViewPager {
public CustomViewPager (Context context) {
super(context);
}
public CustomViewPager (Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onInterceptTouchEvent (MotionEvent ev) {
try {
if (Utilities.getIsZoomed())
return false;
else
return super.onInterceptTouchEvent(ev);
} catch (IllegalArgumentException e) {
return false;
}
}
}
Related
In my Xamarin Forms Android project I am showing a Map using CustomMapRenderer. I am popping up a Info Window in Map screen and this info window has button name called "Call". I need to do a "OnCLick" button click operation for this button. I have googled it, but unfortunately I didn't come across any solutions. at last I have started to work on converting the Android Native code -Java into Xamarin Forms Android - c#, for this conversion I have been using this answers Google Maps Android API v2 - Interactive InfoWindow (like in original android google maps) . This converted code shows A field initializer cannot reference the non static field, method or property OnInfoWindowElemTouchListener.onClickConfirmed(view, marker) this Error inside Java.Lang.Runnable please help me to resolve the problem.
OnInfoWindowElemTouchListener.cs
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.Gms.Maps.Model;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Views;
using Android.Views.Accessibility;
using Java.Lang;
namespace Hotel.Droid
{
public abstract class OnInfoWindowElemTouchListener : Java.Lang.Object
, View.IOnTouchListener
{
private View view;
private Drawable bgDrawableNormal;
private Drawable bgDrawablePressed;
private Handler handler = new Handler();
private Marker marker;
private static bool endPressStatus = false;
private bool pressed = false;
public OnInfoWindowElemTouchListener(View view, Drawable bgDrawableNormal, Drawable bgDrawablePressed)
{
this.view = this.view;
this.bgDrawableNormal = this.bgDrawableNormal;
this.bgDrawablePressed = this.bgDrawablePressed;
}
public OnInfoWindowElemTouchListener()
{
}
public void setMarker(Marker marker)
{
this.marker = this.marker;
}
public bool OnTouch(View vv, MotionEvent e)
{
if (0 <= e.GetX() && e.GetX() <= vv.Width && 0 <= e.GetY() && e.GetY() <= vv.Height)
{
switch (e.ActionMasked)
{
case MotionEventActions.Down:
startPress();
break;
// We need to delay releasing of the view a little so it shows the
// pressed state on the screen
case MotionEventActions.Up:
//handler.PostDelayed(ConfirmClickRunnable, 150);
Task.Factory.StartNew(() => onClickConfirmed(view, marker));
Task.Delay(150);
break;
case MotionEventActions.Cancel:
endPress();
break;
default:
break;
}
}
else {
// If the touch goes outside of the view's area
// (like when moving finger out of the pressed button)
// just release the press
endPress();
}
return false;
}
private void startPress()
{
if (!pressed)
{
pressed = true;
//handler.RemoveCallbacks(ConfirmClickRunnable);
view.SetBackgroundDrawable(bgDrawablePressed);
if ((marker != null))
{
marker.ShowInfoWindow();
}
}
}
public bool endPress()
{
if (pressed)
{
this.pressed = false;
handler.RemoveCallbacks(ConfirmClickRunnable);
view.SetBackgroundDrawable(bgDrawableNormal);
if ((marker != null))
{
marker.ShowInfoWindow();
}
endPressStatus = true;
return true;
}
else {
endPressStatus = false;
return false;
}
}
private Runnable ConfirmClickRunnable = new Java.Lang.Runnable(() =>
{
if (endPressStatus)
{
onClickConfirmed(view, marker);
}
});
/*private class RunnableAnonymousInnerClassHelper : Java.Lang.Object, Java.Lang.IRunnable
{
private readonly Context outerInstance;
public RunnableAnonymousInnerClassHelper(Context outerInstance)
{
this.outerInstance = outerInstance;
}
public void Run()
{
if (endPressStatus)
{
onClickConfirmed();
}
}
}*/
protected abstract void onClickConfirmed(View v, Marker marker);
}
}
Updated
I have implemented the Task.Factory.StartNew instead of Android Runnableand now I am stucking on the below lines. I am struggling on converting this below Java codes into C#since it is written by Anonymous class concept.
Java
this.infoButtonListener = new OnInfoWindowElemTouchListener(infoButton,
getResources().getDrawable(R.drawable.btn_default_normal_holo_light),
getResources().getDrawable(R.drawable.btn_default_pressed_holo_light))
{
#Override
protected void onClickConfirmed(View v, Marker marker) {
// Here we can perform some action triggered after clicking the button
Toast.makeText(MainActivity.this, marker.getTitle() + "'s button clicked!", Toast.LENGTH_SHORT).show();
}
};
this.infoButton.setOnTouchListener(infoButtonListener);
infoButton in code is Call button
C# - Please help me to resolve the problem by converting/using(How to use) the above java code
The solution is too complicated for this window. Please see chat room for step by step solution.
https://chat.stackoverflow.com/rooms/128847/discussion-between-jamal-and-yuri-s
Testing my libGDX app in RoboVM, I have encountered a major problem. When I pause my app (by actually going to the Home screen or sending app invites via Facebook) and then return to my app, classes of my games disappear. As if it does not store data properly on the resume() method. First i though it there was a problem of my AssetLoader, but after some debugging I found that the situation is worse. Actual instances of classes and shapes disappear. As if they never existed.
After googling the issue, I found that it might be related to IOSGraphics, but I have not managed to fix the problem.
My IOSLauncher looks something like this, I have erased the Facebook & Google AdMob specific code.
protected IOSApplication createApplication() {
IOSApplicationConfiguration config = new IOSApplicationConfiguration();
config.useAccelerometer = true;
config.useCompass = true;
config.orientationPortrait = true;
config.orientationLandscape = false;
return new IOSApplication(new Game(this), config);
}
#Override
public boolean didFinishLaunching(UIApplication application,
UIApplicationLaunchOptions launchOptions) {
FBSDKApplicationDelegate.getSharedInstance().didFinishLaunching(application, launchOptions);
initialize();
return true;
}
public void initialize() {
//...
}
public static void main(String[] argv) {
NSAutoreleasePool pool = new NSAutoreleasePool();
UIApplication.main(argv, null, IOSLauncher.class);
pool.close();
}
#Override
public void showAds(boolean show) {
//...
}
#Override
public void shareOnFacebook() {
//...
}
#Override
public void inviteFriends() {
//....
}
#Override
public boolean openURL(UIApplication application, NSURL url,
String sourceApplication, NSPropertyList annotation) {
super.openURL(application, url, sourceApplication, annotation);
return FBSDKApplicationDelegate.getSharedInstance().openURL(
application, url, sourceApplication, annotation);
}
#Override
public void didBecomeActive(UIApplication application) {
super.didBecomeActive(application);
FBSDKAppEvents.activateApp();
}
#Override
public void willResignActive(UIApplication application) {
super.willResignActive(application);
}
#Override
public void willTerminate(UIApplication application) {
super.willTerminate(application);
}
}
This sounds similar to a threading bug I once encountered. I fixed it by deferring resize and resume but I'm not sure if it will help in your case. Something like this:
private boolean needResize, needResume;
private void resize (int width, int height){
needResize = true;
}
private void deferredResize ();
if (!needResize) return;
needResize = false;
int width = Gdx.graphics.getWidth();
int height = Gdx.graphics.getHeight();
//move your resize code here
}
private void resume (){
needResume = true;
}
private void deferredResume (){
if (!needResume) return;
needResume = false;
//move your resume code here
}
public void render (){
deferredResize();
deferredResume();
//...
}
I suggest that you start looking for an alternative to RoboVM to avoid more issues in the future, as Robovm was acquired by Microsoft Xamarin (sad but true) and the framework is no longer maintained. License keys (with Libgdx) will continue to work until the 17th of April 2017, there will be no further updates to RoboVM, be it new features or bug fixes.
As far as I know, Libgdx will switch to Multi-OS engine as the default iOS backend for newly created libGDX projects in the next couple of weeks.
After a couple of days filled with headache I found the solution!
LifeCycle methods like pause & resume, hide & show are not always called When they are supposed to be called, therefore data is not stored properly. This issue can completely break your game.
This thing only occurred when testing my game on the iOS platform, mainly when I opened a 3rd party app, Facebook in this case. No such thing found on Android, though.
The only thing I changed on the iOS version was calling the mentioned methods manually to make sure it always pauses and resumes when it has to.
I am creating an android app (lollipop version). App shows large logo in middle of splash screen. Login screen contains a small sized logo at top. I use ActivityOptions.makeSceneTransitionAnimation() to set animation from large logo of splash screen to small logo of login screen.
Splash screen launches to launch app. After delay of few milisec , splash screen creates intent for login screen. Also set transition. Then it starts login activity. It begins transition animation of logo. And shows login screen successfully. Everything is working well and animation is smooth up to this point.
Then I added "finish();" in splash screen so that back button on login screen do not loads splash screen. Now transition was giving flicker.
I tried following approach but still flicker is there.
used "finishAfterTransition();" instead of "finish();"
added FLAG_ACTIVITY_CLEAR_TOP and FLAG_ACTIVITY_NEW_TASK flags to intent (this leads to even wired behavior)
Override onBackPressed() in login screen. and added
finish();
android.os.Process.killProcess(android.os.Process.myPid()); - This terminates app but restarts it again.
Here, I'm pasting the code of splashScreen. This code is working but gives flicker while transition. gotoLoginScreen() method at the end of class is the place which loads login activity. The login screen is basic activity template from android studio. Using com.android.support:appcompat-v7:21.0.2 library to support lower devices.
public class SplashActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_splash, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
ImageView imageView_logo;
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_splash, container, false);
imageView_logo = (ImageView) rootView.findViewById(R.id.imageview_logo);
imageView_logo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startTimer();
}
});
startTimer();
return rootView;
}
private void startTimer() {
new CountDownTimer(1000, 1000) {
#Override
public void onTick(long l) {
}
public void onFinish() {
launchNextActivity();
}
}.start();
}
/**
* base on session continuity the next activity will be decided and
* launches next activity
*/
private void launchNextActivity() {
if (isSessionContinue()) {
goToHomeScreen();
} else {
goToLoginScreen();
}
}
/**
* checks current user ID, null indicate terminated session
*
* #return true if session is continued and false of session is terminated
*/
private boolean isSessionContinue() {
return false;
}
/**
* directly leads to home screen
*/
private void goToHomeScreen() {
//code to start home screen by skipping login
}
/**
* leads to login screen.
*/
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
**private void goToLoginScreen() {
Intent loginIntent = new Intent(getActivity(), LoginActivity.class);
if (android.os.Build.VERSION.SDK_INT >= 21) {
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(getActivity(), imageView_logo, getString(R.string.transition_logo));
getActivity().startActivity(loginIntent, options.toBundle());
getActivity().finishAfterTransition();
} else {
startActivity(loginIntent);
getActivity().finish();
}
}**
}
}
Is there any way to avoid flicker? It is a stain on beauty.
add this to your activity:
private boolean shouldFinish = false;
#Override
public void onStop() {
super.onStop();
if (shouldFinish) {
getActivity().finish();
}
}
and change this:
private void goToLoginScreen() {
shouldFinish = true;
Intent loginIntent = new Intent(getActivity(), LoginActivity.class);
if (android.os.Build.VERSION.SDK_INT >= 21) {
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(getActivity(), imageView_logo, getString(R.string.transition_logo));
getActivity().startActivity(loginIntent, options.toBundle());
} else {
startActivity(loginIntent);
}
}
I am working on a Windows Phone 8 App which should be protected with a passcode. What is the best way to show the passcode screen everytime the app is lauchend or activated?
I think the central point of action shoule be the App.xaml.cs with its Launch and Activation event handlers. But how exactly can I show the passcode screen?
The problem is, that one never know which pages will be displayed when the app launches or is reactivated. It is either the main page or any other page which was last displayed when the app was deactivated.
I tried to intercept the navigation to the first page, cancel it and show the passcode page instead:
// App.xaml.cs
private void InitializePhoneApplication() {
...
RootFrame.Navigating += HandleFirstNavigation;
...
}
private void HandleFirstNavigation(object sender, NavigatingCancelEventArgs e) {
RootFrame.Navigating -= HandleFirstNavigation;
e.Cancel = true;
RootFrame.Dispatcher.BeginInvoke(new Action(this.OpenPasscodePage));
}
private void OpenPasscodePage() {
RootFrame.Navigate(PasscodePageUri);
}
This works, but only when the app lauchend. When the app reactivated (dormant or tombstoned) the e.Cancel is irgnored. Although the navigation to the passcode page is called the original page is shown.
Moving the navigation the the passcode page from Navigating to Navigated does not worth either:
private void InitializePhoneApplication() {
...
RootFrame.Navigated += PasscodePageAfterFirstNavigation;
...
}
private void PasscodePageAfterFirstNavigation(object sender, EventArgs e) {
RootFrame.Navigated-= PasscodePageAfterFirstNavigation;
RootFrame.Navigate(PasscodePageUri);
}
This seems to be some kind of race condition: Sometimes the passcode page is shown, sometimes the original page. Even if the passcode pages comes up this looks bad because one first see the original page for the fraction of a second before the app navigates further to the passcode page.
Both solution do not work. Any idea what is the right way to implement this?
EDIT: Meanwhile I tried a third solution which does not work either. This solution uses the Uri Mapper:
App.xaml.cs
public bool PasscodeWasConfirmed; private void Application_Launching(object sender, LaunchingEventArgs e) {
...
PasscodeWasConfirmed = false;
...
}
private void Application_Activated(object sender, ActivatedEventArgs e) {
...
PasscodeWasConfirmed = false;
...
}
public Uri InitialPageUri;
public bool ShouldRedirectToPasscodePage(Uri uri) {
if (PasswordWasConfirmend == false) {
InitialPageUri = uri;
return true;
}
return false;
}
UriMapper
public class AppUriMapper : UriMapperBase {
public override Uri MapUri(Uri uri) {
App app = (Application.Current as App);
if (app != null) {
if (app.ShouldRedirectToPasscodePage(uri))
return PasscodeQueryPage.PageUri;
}
// default
return uri;
}
}
PasscodePage
public partial class PasscodePage : PhoneApplicationPage {
...
private void PasscodeConfirmed() {
App app = (Application.Current as App);
app.PasscodeWasConfirmed = true;
NavigationService.Navigate(app.InitialPageUri);
}
}
The Logic is working without any problem, but the app does not navigate to InitialPageUri after the passcode was confirmed. The Uri Mapper is called and correctly and returns the InitialPageUri (no redirect any more). But no navigation happens...
There are no errors, exceptions or debug output. simply nothing happes...
Biggest problem when using Uri Mapper:
When the app is reactivated from Dormant state there is no navigation which could be mapped or redirected...
(I've edited previous answer instead of adding a new one)
I've spend a little time trying to find a solution, and I don't see why your code doesn't run.
In my case it's enough if I do such a change in App.xaml:
private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
{
// Set the root visual to allow the application to render
if (RootVisual != RootFrame)
RootVisual = RootFrame;
// Remove this handler since it is no longer needed
RootFrame.Navigated -= CompleteInitializePhoneApplication;
App.RootFrame.Navigate(new Uri("/passPage.xaml", UriKind.RelativeOrAbsolute));
}
This works on my example which is under the link http://sdrv.ms/1ajH40E
But - I cannot prevent user from seeing last screen when he holds back buton and is chosing to which app return, and then for a blink he can see the last page before leaving the app. I don't know if it is possible to change this behaviour after clicking MS Button:
windows phone change deactivated app image
Second edit
Ok - maybe I've found solution why it sometiems work and sometimes not in your code. After pressing the Start or Search buton the App can go to two cases: Tombstone and non-tombsone. After return different events happen. Code above works with Tombstone case but not with non-tombstone. To work it with the second you need to add (because page is not initialized again) - (of course it can be different solution):
bool afterActivation = false;
private void Application_Activated(object sender, ActivatedEventArgs e)
{
afterActivation = true;
}
private void CheckForResetNavigation(object sender, NavigationEventArgs e)
{
// If the app has received a 'reset' navigation, then we need to check
// on the next navigation to see if the page stack should be reset
if (e.NavigationMode == NavigationMode.Reset)
RootFrame.Navigated += ClearBackStackAfterReset;
if (afterActivation)
{
afterActivation = false;
App.RootFrame.Navigate(new Uri("/passPage.xaml", UriKind.RelativeOrAbsolute));
}
}
Please also ensure of your debug properties in VS: Project->Properties->Debug->Tombstone upon deactiovation checkbox.
You can also find some information here (if you haven't seen it before):
http://blogs.msdn.com/b/ptorr/archive/2010/12/11/how-to-correctly-handle-application-deactivation-and-reactivation.aspx
I have Swing application which manipulate shapes. In my toolbar I have a zoom function that the user activate by clicking on a button, then the cursor of the mouse changes to a magnifier which is an image.
My problem is actually the cursor, for some raisons, when I set the cursor on the panel displaying the shapes, I can't save my model and I get the java.io.NotSerializableException: sun.awt.image.ToolkitImage exception.
My model
public class Document implements IDocObservable,Serializable{
...
public void updateCursor() {
Iterator<IDocObserver> iter = docObservers.iterator();
while (iter.hasNext()) {
iter.next().docCursorChanged();
}
}
...
}
The action
public class ZoomInAction extends AbstractAction {
public void actionPerformed(ActionEvent arg0) {
...
Application.getInstance().getActiveDocument().updateCursor();
}
}
The display Panel (note : if I comment the setCursor(..) line, I'am able to save )
public class Window extends JPanel implements IDocObserver{
...
public void paint(Graphics g){
//drawing the differents shapes
}
#Override
public void docCursorChanged() {
setCursor(Utile.getZoomInCursor();
}
}
}
The class that provide the cursor
public class Utile {
private static Image zoomIn = toolkit.getImage(Utile.class.getResource("/images/zoomin_mouse.png"));
...
public static Cursor getZoomInCursor() {
return toolkit.createCustomCursor(zoomIn, hotSpot, "");
}
}
The writing of the object is a standard Java methode with outStream.writeObject(doc);
thanks
You aren't just serializing a model, you are serializing a list of IDocObservers, which includes Window extends JPanel implements IDocObserver. IOW you are serializing a JPanel. Don't do that: see the warning at the top of the Javadoc. You don't need to save the observers along with the observable, surely: can't you make that list transient?