I am using ViewFlipper to flip two images. one image is a small heart and another is a big heart and they are flipping continuously so that it looks like heart is throbbing.
everything works well except one thing
when the Activity containing ViewFlipper is on the front and the ViewFlipper is flipping once i called the startFlippling , and after a while if my device's light gets dim and then turned off and if i press the home/power button immediately the ViewFlipper stops flipping. in this case no onResume or onPause gets called.
moreover when the device's light gets turned off and after a while if i press power button and entered my password to unlock the screen , then the ViewFlipper is working fine.
here is my layout code , i have set the autoStart to false because based on some event
i am calling startFlipping of ViewFlipper
<ViewFlipper
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoStart="false"
android:flipInterval="700"
android:id="#+id/heartThrob"
android:layout_centerHorizontal="true"
android:layout_centerInParent="true"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/small"
android:id="#+id/heartOne"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/big"
android:id="#+id/heartTwo"
/>
</ViewFlipper>
Rite now i have set the ViewFlipper property android:keepScreenOn to true so that the screen remain ON , but i am still waiting for some better answer
I had the same issue if I use viewFlipper's startFlipping with setFlipInterval methods. So I've solved this by using a handler to animate viewFlipper.
By the way, this problem occurs on Galaxy Nexus 4.0.1 but not on Galaxy Nexus 4.2.2.
I hope this part of my code helps you.
private Handler handler = new Handler();
private Runnable autoSwipeRunnable = new Runnable() {
#Override
public void run() {
moveLeft();
}
};
private void moveLeft() {
if (mViewFlipper != null && mViewFlipper.getChildCount() > 1) {
mViewFlipper.setInAnimation(animLeftIn);
mViewFlipper.setOutAnimation(animLeftOut);
mViewFlipper.showNext();
startAutoCycle();
}
}
private void startAutoCycle() {
if (mViewFlipper.getChildCount() > 1) {
if (autoCycleInterval <= 0) {
autoCycleInterval = 5000;
}
handler.removeCallbacks(autoSwipeRunnable);
handler.postDelayed(autoSwipeRunnable, autoCycleInterval);
}
}
Related
I have an XML like this (I simplified from the original one, but it still reproduces the issue):
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="#+id/xContainer"
android:clickable="true" // does not help
android:focusable="true" // does not help
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingBottom="8dp"
android:paddingTop="8dp">
<TextView
android:duplicateParentState="true" // does not help
android:clickable="false" // does not help
android:id="#+id/xText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="8dp"
android:text="#string/xText"
android:textSize="14sp" />
</LinearLayout>
</merge>
and it's included from a CardView like this:
<include layout="#layout/other_stuff_first" />
<include layout="#layout/my_clickable_layout" />
Then I display the item dynamically and attach an OnClickListener to it, in the same method:
public class XViewHolder extends RecyclerView.ViewHolder {
private final View xContainer;
...
public XViewHolder(#NonNull View itemView) {
xContainer = itemView.findViewById(R.id.xContainer);
}
...
private void bindStuff(RecyclerView.ViewHolder holder, int position) {
if (something) {
xContainer.setVisibility(View.VISIBLE);
xContainer.setOnClickListener(v -> {
// do some stuff
});
} else {
xContainer.setVisibility(View.GONE);
xContainer.setOnClickListener(null);
}
}
}
public class ParentAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
...
((XViewHolder) xViewHolder).bindStuff();
}
}
I put some breakpoints, went through the code in the debugger, and it's properly executed (otherwise it would not show up in the UI at all), so the listener is set.
However, the first time when I click the container, most of the time nothing happens. On the consecutive clicks, it works (90% of time on 2nd click and 100% of time on 3rd+ click). I always click in the same way, the container is big, even if I put my whole finger down with a big force, first click doesn't register.
I found several similar questions, but they're either "all or nothing" (whereas in my case it's just sporadic), or due to some other issues. In my app, there's no work on the main thread after the UI is displayed.
Any ideas what can be wrong?
Edit: after some investigation, the issue only happens if:
my_clickable_layout is at the bottom of the scrolling container and not initially visible on screen (only becomes visible after the user scrolls down), AND
the user scrolls down fast, AND
the user taps my_clickable_layout within 0-3 seconds after the scroll.
If the layout is immediately visible, OR user scrolls down slowly, OR user taps more than once, then the taps are dispatched properly.
The RecyclerView itself receives the touch events, because I can always see the logs from the code below, even if I don't see the logs from my children listeners.
recyclerView = view.findViewById(R.id.recycler_view);
recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
Timber.w("onInterceptTouchEvent");
return false;
}
});
At this moment I believe this might be a bug in RecyclerView, which wrongly consumes the first event after a fast scroll - perhaps to detect "end of scrolling" tap?
I want to turn off touches on the screen just for 1 or 2 seconds and then turn on. How to do this with a background service as an infinite loop? For instance, if you touch once you cannot touch again in 2 seconds in anywhere on the screen and then keep doing same thing for every touches (not include back, menu or home buttons).
Note: I am using Service for background progress.
You might could do it this way. Disable all views (you can iterate over all children of your root layout) and enable them after two seconds.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final RelativeLayout root = (RelativeLayout) findViewById(R.id.activity_main);
final Button btnDisable = (Button) findViewById(R.id.btnDisable);
final Handler handler = new Handler();
btnDisable.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
root.setEnabled(false);
btnDisable.setEnabled(false);
handler.postDelayed(new Runnable() {
#Override
public void run() {
//enable your views after 2 seconds
root.setEnabled(true);
btnDisable.setEnabled(true);
}
}, 2000);
}
});
}
Here is the xml inside of a RelativeLayout with the id activity_main
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<Button
android:text="disable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/btnDisable"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
if you want to disable touch only in your app consider bounding service and communicate with your Activity and override MotionEvent related methods, e.g.
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
// your code goes here if needed
return true; //disabled touching in whole activity
}
if you want to disable touching in whole system then this is obvoiusly impossible, because is highly unsecure for user...
I would like to create a loop somewhere in my Android code that changes the color of a drawable rectangle between two colors continuously at some rate. I would like to start and stop its blinking using two buttons. I have done a lot of research, but just can't seem to figure out how to do it. I am new to android and do not have experience with run() methods. But I am guessing I have to make some kind of rectangle class with a run() method that will animate it into changing colors.
I am also fairly new to android, but I will give it a shot.
Since you say you want it to blink, you should be able to switch the actual image between, lets say, blue and red, with a simple 'for' loop. When the button is pressed, you could change the status of a boolean from false to true. Then, when the 'for' statement is not true anymore, it jumps to the next set of code, which stops it. Here is what I would do.
Your XML for the two buttons:
<Button
android:id="#+id/start"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Start"
android:Clickable="true"
android:onClick="start"
/>
<Button
android:id="#+id/stop" <!-- Gives the button an ID to use in java code -->
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Stop" <!-- sets the text on the button -->
android:Clickable="true" <!-- makes the button clickable -->
android:onClick="stop" <!-- The method it calls when it is clicked, removes the necessity of an OnClickListener -->
/>
Now, you would have 2 ImageViews: blue_rectangle and red_rectangle, both in the same place in the layout. Here is the XML for the two ImageViews
<ImageView
android:id="#+id/blue_rectangle"
android:layout_width="100dp"
android:layout_height="75dp"
android:layout_marginLeft="50dp"
android:layout_marginTop="5dp"
android:src="#drawable/blue_rectangle" />
<ImageView
android:id="#+id/red_rectangle"
android:layout_width="100dp"
android:layout_height="75dp"
android:layout_marginLeft="50dp"
android:layout_marginTop="5dp"
android:src="#drawable/red_rectangle" />
This next part is the tricky part.
Here is the Java.
package your.package.name.thingy.here;
//everything it tells you to import goes here
public class BlinkingActivity extends Activity{
ImageView blueRectangle;
ImageView redRectangle;
Button start;
Button stop;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.yourLayout);
blueRectangle = (ImageView) findViewById(R.id.blue_rectangle);
redRectangle = (ImageView) findViewById(R.id.red_rectangle);
start = (Button) findViewById(R.id.start);
stop = (Button) findViewById(R.id.stop);
Boolean isBlinking = new Boolean(false);
blinkRectangle(whateverVariableThisNeeds);
}
public static void blinkRectangle(View view){
blueRectangle.setVisibility(View.INVISIBLE);
redRectangle.setVisibility(View.INVISIBLE);
for(initialization; isBlinking; update){
blueRectangle.setVisibility(View.VISIBLE);
blueRectangle.postDelayed(new Runnable() {
#Override
public void run() {
blueRectangle.setVisibility(View.INVISIBLE);
}
}, 2000); //the 2000 is the number of milliseconds for how long blue is visible
redRectangle.setVisibility(View.VISIBLE);
redRectangle.postDelayed(new Runnable() {
#Override
public void run(){
redRectangle.setVisibility(View.INVISIBLE);
}
}, 2000);
blueRectangle.setVisibility(View.VISIBLE); //This prevents a bug where if the user hits the stop button at just the right time, both rectangles will be invisible.
}
public static void start(View view){ //no need to call this anywhere, the XML onClick does it for you
isBlinking = true; //I think this is how you set a boolean, if not, correct me.
}
public static void stop(View view){//same here
isBlinking = false; //again, correct me if I'm wrong.
}
}
Here is what the code basically does.
It has a boolean which is defaulted to false. While it is false, the rectangle does not blink. While it is true, the for statement in blinkRectangle() runs. That for loop makes the blue on visible, waits 2 seconds, makes it invisible, makes the red one visible, waits two seconds, and repeats. The start() and stop() methods switch the boolean to true and false, respectively. I think this type of for loop re-checks the boolean when it gets back to the top. I have never worked with it before. That's what I could gather from the website I looked at.
Well, I think that about does it. If you don't understand what any of the code does, or it doesn't work, or I have the question wrong, or I am just downright wrong, or ANYTHING, just comment on this answer. I hope this works!
Good Luck!
P.S. Here are the websites I used for reference
http://www.tutorialspoint.com/java/java_loop_control.htm
http://www.java-examples.com/java-boolean-example
Wow...I just realized this question is 2 years old. Still, I hope this helps you!
This question has been asked several times, but everyone gives the reason for why this occurs (i.e. calculation occurs before the layout is laid). But I need the solution for this. I tried getBottom();, getTop();, getLocationOnScreen(int[] location);. But all returns the value Zero (0). I even tried giving these in onStart();, to give time for layout to be laid, but no luck.
Here is my code:
TextView tv;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tv = (TextView) findViewById(R.id.textView1);
Log.d("getBottom :", Integer.toString(tv.getBottom()));
Log.d("gettop :", Integer.toString(tv.getTop()));
int[] location = new int[2];
tv.getLocationOnScreen(location);
Log.d("getlocationonscreen values :", Integer.toString(location[0])+" "+Integer.toString(location[1]));
}
#Override
protected void onStart() {
Log.d("getBottom in onStart :", Integer.toString(tv.getBottom()));
Log.d("gettop in onStart :", Integer.toString(tv.getTop()));
int[] location = new int[2];
tv.getLocationOnScreen(location);
Log.d("getlocationonscreen in onStart :", Integer.toString(location[0])+" "+Integer.toString(location[1]));
super.onStart();
}
Layout XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="156dp"
android:text="Medium Text"
android:textAppearance="?android:attr/textAppearanceMedium" />
</RelativeLayout>
Again, I apologize for repeating the question. Thanks in advance.
You put the 'measuring' in the onWindowFocusChanged()-method.
As the documentation states:
This is the best indicator of whether this activity is visible to the user.
You could also put it in the onResume() which is the last step before the application is completely on screen and active, however:
Keep in mind that onResume is not the best indicator that your activity is visible to the user; a system window such as the keyguard may be in front. Use onWindowFocusChanged(boolean) to know for certain that your activity is visible to the user (for example, to resume a game).
If the window/view has not yet been displayed there is no guarantee that it has its measurements, thus the previous method would be better.
This was asked elsewhere, but the solution did not work for me. So posing it again with more context. The issue is that an activity contains a scrolling music title text view which is disrupted by an updating elapsed time counter text view.
I have these two TextView widgets in my activity layout (although they are encompassed by other layout containers).
<TextView android:id="#+id/v_current_time"
android:minWidth="26dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="right|center_vertical"
android:singleLine="true"
android:textSize="12sp"
android:enabled="false"
/>
<TextView android:id="#+id/v_track_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="normal"
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:enabled="true"
/>
The music title is dynamically set to (in the test case) a long line of text. If I never update the text for the current time with the following line, the music title will happily scroll forever no matter how I interact with my pause and play buttons.
tvCurrentTime.setText(DataFormat.formatTime(progress));
But if I do set the text for the current time, the music title will stop. Pressing my pause button somehow kicks scrolling back into gear, but pressing play will cause the current time to update and stop it again.
Per the suggestion in this thread, I attempted to couple the setting of the time text with re-enabling of the scrolling title as follows:
tvCurrentTime.setText(DataFormat.formatTime(progress));
tvTitle.setEnabled(true);
This has no effect on the failure other than to reset the scroller each time it restarts.
Is there some detail that I am missing, or any other thoughts as to what could be going wrong? Thanks a lot!
There is another way to solve this without removing all RelativeLayout.
You can simply wrap the marquee TextView with a LinearLayout inside the RelativeLayout as a container.
Marquee is problematic. When TextView (or the Window containing the TextView)loses focus the marquee is stopped and reset (see the sources for TextView).
I guess you have 3 possible solutions here:
You can set android:focusable="false" in all other Views in your layout. That way your TextView shouldn't lose focus. But that's probably not the solution you want.
You can subclass TextView and change onFocusChanged() and onWindowFocusChanged() to prevent marquee from being stopped and reset.
Create your own implementation of marquee.
(Promoting comment above to an answer) Turns out that the TextView XML configs above work fine without any runtime changes (to reset enabled=true or whatever) IF I get rid of the RelativeLayout's in my layout file and replace them with LinearLayout's. And neither suggestion 1 or 2 above (not sure about 3) works if I don't. That seems like a subtle and bogus undocumented failure of RelativeLayout.
In java code, doing tvTitle.setSelected(true); (here, tvTitle is your sliding TextView variable) worked for me. Doing this, seems to make it focused again. So worked like a charm.
We had an adapter with multiple view types, and first item was one with marquee TextView. After scrolling back to top of the screen text was not shown (we've called textView.isSelected == true).
Also, issue was not the RelativeLayout, there was no need to wrap TextView with LinearLayout, as current structure in layout is:
RelativeLayout
Button
TextView
Below is method from TextView to start marquee:
private void startMarquee() {
// Do not ellipsize EditText
if (getKeyListener() != null) return;
if (compressText(getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight())) {
return;
}
if ((mMarquee == null || mMarquee.isStopped()) && (isFocused() || isSelected())
&& getLineCount() == 1 && canMarquee()) {
if (mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_FADE;
final Layout tmp = mLayout;
mLayout = mSavedMarqueeModeLayout;
mSavedMarqueeModeLayout = tmp;
setHorizontalFadingEdgeEnabled(true);
requestLayout();
invalidate();
}
if (mMarquee == null) mMarquee = new Marquee(this);
mMarquee.start(mMarqueeRepeatLimit);
}
}
It requires for view to have focus or to be selected to start marquee.
In TextView.onFocusChanged(...) the startStopMarquee(focused) method is called and it will trigger the scroll animation. Issue we had with this approach is that we needed to request focus by using postDelayed, which might cause some issues.
After checking what TextView.setSelected(boolean) method was doing, it was clear why textView.isSelected = true was not triggering animation. Inside it it was checking previous isSelected state, and it would handle startMarquee() or stopMarquee() if new isSelected state was different from the previous one.
Solution was to change selected state to false and after that to set it to true which worked perfectly.
Below are both methods, setSelected, and onFocusChanged.
#Override
public void setSelected(boolean selected) {
boolean wasSelected = isSelected();
super.setSelected(selected);
if (selected != wasSelected && mEllipsize == TextUtils.TruncateAt.MARQUEE) {
if (selected) {
startMarquee();
} else {
stopMarquee();
}
}
}
#Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (isTemporarilyDetached()) {
// If we are temporarily in the detach state, then do nothing.
super.onFocusChanged(focused, direction, previouslyFocusedRect);
return;
}
if (mEditor != null) mEditor.onFocusChanged(focused, direction);
if (focused) {
if (mSpannable != null) {
MetaKeyKeyListener.resetMetaState(mSpannable);
}
}
startStopMarquee(focused);
if (mTransformation != null) {
mTransformation.onFocusChanged(this, mText, focused, direction, previouslyFocusedRect);
}
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}