How to scroll a Pager programatically AND smoothly - user-interface

I'm using this code to scroll programatically my pager
public void MoveNext(View view) {
pager.setCurrentItem(pager.getCurrentItem() + 1);
}
public void MovePrevious(View view) {
pager.setCurrentItem(pager.getCurrentItem() - 1);
}
The code work perfect but the transition is too fast. How can I introduce a delay so that the scrolling would be done more smoothly?

I believe there are two ways can achieve this goal, I've tried by ViewPager's fakeDrag() but which doesn't work perfect, luckly, another way does, by simulate touch motion event and use ObjectAnimator to specify the animation duration then make control the scroll speed become true.
public class ViewPagerActivity extends FragmentActivity
implements View.OnClickListener, Animator.AnimatorListener {
private ViewPager mViewPager;
private View btnTriggerNext;
private View btnTriggerPrev;
#Override
protected void onCreate(...) {
super...;
setContentView(R.layout.layout_xml);
mViewPager = findViewById(...);
btnTriggerNext = findViewById(R.id.btnTriggerNext);
btnTriggerNext.setOnClickListener(this);
btnTriggerPrev = findViewById(R.id.btnTriggerPrev);
btnTriggerPrev.setOnClickListener(this);
}
private boolean mIsInAnimation;
private long mMotionBeginTime;
private float mLastMotionX;
#Override
public void onClick(View v) {
if (mIsInAnimation) return;
ObjectAnimator anim;
if (v == btnTriggerPrev) {
if (!hasPrevPage()) return;
anim = ObjectAnimator.ofFloat(this, "motionX", 0, mViewPager.getWidth());
}
else if (v == btnTriggerNext) {
if (!hasNextPage()) return;
anim = ObjectAnimator.ofFloat(this, "motionX", 0, -mViewPager.getWidth());
}
else return;
anim.setInterpolator(new LinearInterpolator());
anim.addListener(this);
anim.setDuration(300);
anim.start();
}
public void setMotionX(float motionX) {
if (!mIsInAnimation) return;
mLastMotionX = motionX;
final long time = SystemClock.uptimeMillis();
simulate(MotionEvent.ACTION_MOVE, mMotionBeginTime, time);
}
#Override
public void onAnimationEnd(Animator animation) {
mIsInAnimation = false;
final long time = SystemClock.uptimeMillis();
simulate(MotionEvent.ACTION_UP, mMotionBeginTime, time);
}
#Override
public void onAnimationStart(Animator animation) {
mLastMotionX = 0;
mIsInAnimation = true;
final long time = SystemClock.uptimeMillis();
simulate(MotionEvent.ACTION_DOWN, time, time);
mMotionBeginTime = time;
}
// method from http://stackoverflow.com/a/11599282/1294681
private void simulate(int action, long startTime, long endTime) {
// specify the property for the two touch points
MotionEvent.PointerProperties[] properties = new MotionEvent.PointerProperties[1];
MotionEvent.PointerProperties pp = new MotionEvent.PointerProperties();
pp.id = 0;
pp.toolType = MotionEvent.TOOL_TYPE_FINGER;
properties[0] = pp;
// specify the coordinations of the two touch points
// NOTE: you MUST set the pressure and size value, or it doesn't work
MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[1];
MotionEvent.PointerCoords pc = new MotionEvent.PointerCoords();
pc.x = mLastMotionX;
pc.pressure = 1;
pc.size = 1;
pointerCoords[0] = pc;
final MotionEvent ev = MotionEvent.obtain(
startTime, endTime, action, 1, properties,
pointerCoords, 0, 0, 1, 1, 0, 0, 0, 0);
mViewPager.dispatchTouchEvent(ev);
}
private boolean hasPrevPage() {
return mViewPager.getCurrentItem() > 0;
}
private boolean hasNextPage() {
return mViewPager.getCurrentItem() + 1 < mViewPager.getAdapter().getCount();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
}
cause it was simulating touch event, so please use a proper duration(less than 600ms will be nice) to do scrolling, when scroll in progress, put down finger would stop it and cause some bugs.

Change
pager.setCurrentItem(pager.getCurrentItem() + 1);
to
pager.setCurrentItem(pager.getCurrentItem() + 1,true);
This will call the method setCurrentItem(int item,boolean smoothScroll) which will scroll smoothly to the mentioned item instead of transitioning immediately

Related

Code Error. The modifier public is not valid. Why?

I am making a 2D game in Unity and am trying to make my moveable character stop every time dialogue appears on screen.
I am using the Fungus extension for my dialogue as I'm a newbie to coding. Every thing I try however I run in to problems.
My current issue is that the modifier 'public' is not valid for this item.
Anyone know how this can be fixed? I have attached the code below. I assume the issue is with the public void CantMove() and public void CanMove() lines.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float moveSpeed;
public Rigidbody2D theRB;
public float jumpForce;
private bool isGrounded;
public Transform groundCheckPoint;
public LayerMask whatIsGround;
private bool canDoubleJump;
private bool canMove = true;
private Animator anim;
private SpriteRenderer theSR;
// Start is called before the first frame update
void Start()
{
anim = GetComponent<Animator>();
theSR = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void Update()
{
if(!canMove)
{
theRB.velocity = new Vector2(0, 0);
}
else
{
theRB.velocity = new Vector2(moveSpeed * Input.GetAxis("Horizontal"), theRB.velocity.y);
}
public void CantMove()
{
canMove = false;
}
public void CanMove()
{
canMove = true;
}
//theRB.velocity = new Vector2(moveSpeed * Input.GetAxis("Horizontal"), theRB.velocity.y);
isGrounded = Physics2D.OverlapCircle(groundCheckPoint.position, .2f, whatIsGround);
if(isGrounded)
{
canDoubleJump = true;
}
if(Input.GetButtonDown("Jump"))
{
if (isGrounded)
{
theRB.velocity = new Vector2(theRB.velocity.x, jumpForce);
}
else
{
if(canDoubleJump)
{
theRB.velocity = new Vector2(theRB.velocity.x, jumpForce);
canDoubleJump = false;
}
}
}
if(theRB.velocity.x > 0)
{
theSR.flipX = true;
} else if(theRB.velocity.x < 0)
{
theSR.flipX = false;
}
anim.SetFloat("moveSpeed", Mathf.Abs( theRB.velocity.x));
anim.SetBool("isGrounded", isGrounded);
}
}
'''
Your problem is that your two functions defined for CanMove and CantMove are declared inside of the Update function body... which makes them locally scoped functions which means they can never have public access and can only be called from within the Update function itself.
Move these two functions outside of the Update function body like this...
void Update() {
...
}
public void CantMove() {
canMove = false;
}
public void CanMove() {
canMove = true;
}

Animation flashing

I continuously have this problem with all my most recent animation projects. Every time I run my animations, they never seem to be completely visible and full, but rather they blink and flash similar to a light bulb that is not fully screwed in ( i know, strange comparison but I can't think of what else it resembles). I feel like it must have something to do with my placement of repaint(); but I'm just not sure at this point. On a previous animation I made, the problem was that my "private BufferedImage offScr" variable wasn't set correctly, but viewing other programs similar to the one I am working on now, I don't see why that variable would be necessary. Thanks for all your help folks, and I apologize for my lack of knowledge in programming vocabulary.
Here is my program so far:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
public class DoveAnimator extends JFrame implements ActionListener {
private int DELAY = 40; // the delay for the animation
private final int WIDTH = 400; // the window width
private final int HEIGHT = 180; // the window height
private final int IMAGEAMT = 8;
private Image [] doveLeft = new Image[IMAGEAMT];
private Image [] doveRight = new Image[IMAGEAMT];
private int doveIndex = 0;
private boolean isRight = true;
private JPanel dovePanel;
private Image dove;
private JButton slowerButton = new JButton ("Slower");
private JButton fasterButton = new JButton ("Faster");
private JButton reverseButton = new JButton ("Reverse");
private JButton pauseResumeButton = new JButton (" pause ");
private Timer timer;
private int clicks = 2;
private boolean pause = false;
/** The constructor */
public DoveAnimator() {
MediaTracker track = new MediaTracker(this);
for (int i = 0; i < IMAGEAMT; ++i) {
doveLeft[i] = new ImageIcon("doves/ldove" + (i+1) + ".gif").getImage();
doveRight[i] = new ImageIcon("doves/rdove" + (i+1) + ".gif").getImage();
track.addImage(doveLeft[i],0);
track.addImage(doveRight[i],0);
}
// dove = doveRight[0];
//track.addImage(bkgImage,0);
// track.addImage(dove,0);
try {
track.waitForAll();
} catch ( InterruptedException e ) { }
dove = doveRight[0];
JPanel mainPanel = new JPanel();
mainPanel.setPreferredSize(new Dimension(WIDTH, HEIGHT));
setTitle ("Dove Animator");
dovePanel = new JPanel();
dovePanel.setPreferredSize(new Dimension(100, 125));
dovePanel.setBackground(Color.WHITE);
mainPanel.add(dovePanel);
JPanel buttonPanel = new JPanel();
buttonPanel.setPreferredSize(new Dimension(WIDTH, 40));
// button 1
slowerButton.addActionListener (this);
buttonPanel.add (slowerButton);
// button 2
fasterButton.addActionListener (this);
buttonPanel.add (fasterButton);
// button 3
reverseButton.addActionListener (this);
buttonPanel.add (reverseButton);
// button 4
pauseResumeButton.addActionListener (this);
buttonPanel.add (pauseResumeButton);
mainPanel.add(buttonPanel);
add(mainPanel);
setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
setVisible (true);
pack();
timer = new Timer(DELAY,this); // setting timer delay
timer.start(); // start the timer
}
public void switchDove() {
++doveIndex;
if (doveIndex >= IMAGEAMT)
doveIndex = 0;
if (isRight)
dove = doveRight[doveIndex];
else
dove = doveLeft[doveIndex];
dove = (isRight) ? doveRight[doveIndex] : doveLeft[doveIndex];
}
/** Handler for button clicks and timer events */
public void actionPerformed (ActionEvent evt) {
if (evt.getSource() == slowerButton)
{
DELAY += 10;
}
else if (evt.getSource() == reverseButton)
{
if(evt.getSource() == reverseButton && isRight == true){
isRight = false;
}
else if(evt.getSource() == reverseButton && !isRight){
isRight = true;
}
}
else if (evt.getSource() == fasterButton)
{
DELAY -= 10;
if (DELAY <= 10 ){
DELAY = 10;
}
}
else if (evt.getSource() == pauseResumeButton)
{
if(evt.getSource() == pauseResumeButton && !pause){
pauseResumeButton.setText(" Resume ");
timer.stop();
pause = true;
}
else if(evt.getSource() == pauseResumeButton && pause == true){
pauseResumeButton.setText(" Pause ");
timer.start();
pause = false;
}
}
else if (evt.getSource() == timer)
{
drawAnimation();
switchDove();
repaint();
}
}
/** Draws the dove in the dovePanel */
public void drawAnimation() {
Graphics page = dovePanel.getGraphics();
page.drawImage(dove,0,0,Color.WHITE,null);
}
/** The main method */
public static void main (String [] args) {
new DoveAnimator();
}
}
Yep it seems to be one of your repaint() calls. Take out your repaint() method call here:
else if (evt.getSource() == timer)
{
drawAnimation();
switchDove();
repaint();
It's confusing the program because you are already switching the doves. That's my best guess. I ran it and it seems to work though. Cheers!
Also I just noticed that the way you are adjusting your timer will yeild you no results. You need to use the command: timer.setDelay(newDelay); you can also put arguments in the parenthesis like timer.setDelay(DELAY -= 10);

Fragment with update of detail layout

I have programmed an Android App with fragments. A ListView fragment and a detail fragment.
What I wanna do is, if someone clicks inside the detail activity, a layout which is "View.Gone" should be "View.Visible". The code works without errors but nothing changed on the screen.
You can see it in Detail fragment code where a clik event on the ImageButton btn is.
What do i wrong?
What is the best way to update the detail screen? If someone has a small example or could write me where in my code I have to change what, it makes me happy :-)
Thanks a lot
Tom
The FragmentActivity:
public class CacheFragment extends SherlockFragmentActivity {
CacheListFragment f;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_cachelist);
f = new CacheListFragment();
// Supply index input as an argument.
Bundle args = new Bundle();
f.setArguments(args);
}
Here's the Detail Fragment, where you can see whatt should happen if someone clicks on the Imagebutton:
public class CacheDetailsFragment extends SherlockFragment implements OnClickListener {
private CacheDetailsLoading cdLoad= new CacheDetailsLoading();
private static GeocacheDetails _cacheDetails = new GeocacheDetails();
private static GCRatingTyp _cacheVote = new GCRatingTyp();
private CacheDetailsUsing cdUsing = new CacheDetailsUsing();
private Activity _context;
private static CacheDetailsFragment f;
private View view;
/**
* Create a new instance of DetailsFragment, initialized to
* show the text at 'index'.
*/
public static CacheDetailsFragment newInstance(int index ) {
f = new CacheDetailsFragment();
// Supply index input as an argument.
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
public int getShownIndex() {
return getArguments().getInt("index", 0);
}
public void setCacheDetail(GeocacheDetails cacheDetails)
{
_cacheDetails = cacheDetails;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (container == null) {
return null;
}
Bundle bundle=getArguments();
_cacheVote= bundle.getParcelable("cacheVote");
int index = bundle.getInt("index");
_cacheDetails=StaticCacheListByGroup.getCacheList().get(index);
_context = getActivity();
_context.setTheme(R.style.Theme_Sherlock_Light_DarkActionBar);
view = inflater.inflate(R.layout.list_cachedetails, container,false);
((RelativeLayout) view.findViewById(R.id.relativeLoggingInfo)).setVisibility(View.GONE);
((RelativeLayout) view.findViewById(R.id.relativeKategorienInfo)).setVisibility(View.GONE);
ImageButton btn = (ImageButton) view.findViewById(R.id.description_expand);
btn.setOnClickListener(new OnClickListener() {
public void onClick(View v)
{
if(((RelativeLayout) getActivity().findViewById(R.id.relativeDescriptionInfo)).getVisibility() == View.GONE)
{
((ImageButton) getActivity().findViewById(R.id.description_expand)).setBackgroundResource(R.drawable.navigation_collapse_dark);
((RelativeLayout) getActivity().findViewById(R.id.relativeDescriptionInfo)).setVisibility(View.VISIBLE);
}
else
{
((ImageButton) getActivity().findViewById(R.id.description_expand)).setBackgroundResource(R.drawable.navigation_expand_dark);
((RelativeLayout) getActivity().findViewById(R.id.relativeDescriptionInfo)).setVisibility(View.GONE);
}
});
return view;
}
}
Now the Listfragment:
public class CacheListFragment extends SherlockListFragment {
boolean isDualPane;
int mCurCheckPosition = 0;
private CacheListArrayAdapter _adapter;
private SharedPrefs _sp= new SharedPrefs();
private double latitude=0;
private double longitude=0;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
latitude =Double.parseDouble(_sp.getSharedPrefs(getActivity(), LibraryDefaults.PROGRAMMNAME, "Latitude", "0"));
longitude =Double.parseDouble(_sp.getSharedPrefs(getActivity(), LibraryDefaults.PROGRAMMNAME, "Longitude", "0"));
// Check to see if we have a frame in which to embed the details
// fragment directly in the containing UI.
View detailsFrame = getActivity().findViewById(R.id.details);
isDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE;
if(!isDualPane)
{
Bundle bundle = getActivity().getIntent().getExtras();
if(bundle != null && bundle.containsKey("Titel"))
((TextView) getActivity().findViewById(R.id.listtitle)).setText(bundle.getString("Titel"));
else
((TextView) getActivity().findViewById(R.id.listtitle)).setText(this.getResources().getString(R.string.caches_listtitle));
}
if (StaticCacheListByGroup.getCacheList() != null)
{
GeocachingCompass gc = new GeocachingCompass(getActivity());
_adapter = new CacheListArrayAdapter(getActivity(), StaticCacheListByGroup.getCacheList(), longitude,latitude);
_adapter.setActualCoordinates(new LatLng(latitude,longitude));
_adapter.setActualHeading(gc.getBearing(latitude,longitude));
if (_adapter != null)
setListAdapter(_adapter);
if (savedInstanceState != null) {
// Restore last state for checked position.
mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
}
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
getListView().setSoundEffectsEnabled(true);
getListView().setSmoothScrollbarEnabled(true);
getListView().setDrawSelectorOnTop(false);
getListView().setCacheColorHint(R.color.transparentBlack);
getListView().setDivider(getResources().getDrawable( R.color.divider));
getListView().setDividerHeight(5);
if (isDualPane) {
// In dual-pane mode, the list view highlights the selected item.
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
showDetails(mCurCheckPosition);
}
}
}
#Override
public void onResume() {
super.onResume();
GeocachingCompass gc = new GeocachingCompass(getActivity());
_adapter = new CacheListArrayAdapter(getActivity(), StaticCacheListByGroup.getCacheList(), longitude,latitude);
_adapter.setActualCoordinates(new LatLng(latitude,longitude));
_adapter.setActualHeading(gc.getBearing(latitude,longitude));
if (_adapter != null)
setListAdapter(_adapter);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("curChoice", mCurCheckPosition);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
showDetails(position);
}
/**
* Helper function to show the details of a selected item, either by
* displaying a fragment in-place in the current UI, or starting a
* whole new activity in which it is displayed.
*/
void showDetails(int index) {
mCurCheckPosition = index;
ReadGCVote getVote = new ReadGCVote();
GeocacheDetails cacheDetails = new GeocacheDetails();
cacheDetails=StaticCacheListByGroup.getCacheList().get(index);
if (isDualPane) {
// We can display everything in-place with fragments, so update
// the list to highlight the selected item and show the data.
getListView().setItemChecked(index, true);
// Check what fragment is currently shown, replace if needed.
CacheDetailsFragment details = (CacheDetailsFragment)
getActivity().getSupportFragmentManager().findFragmentById(R.id.details);
if (details == null || details.getShownIndex() != index) {
// Make new fragment to show this selection.
details = CacheDetailsFragment.newInstance(index);
// Execute a transaction, replacing any existing fragment
// with this one inside the frame.
FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.replace(R.id.details, details);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
} else {
// Otherwise we need to launch a new activity to display
Intent intent = new Intent();
intent.setClass(getActivity(), CacheDetailsActivity.class);
intent.putExtra("index", index);
intent.putExtra("cacheDetails",cacheDetails);
intent.putExtra("cacheVote",getVote.getGCVoteByCacheGuid( StaticGCVoteList.getCacheList(), cacheDetails.GetGUID()));
startActivity(intent);
}
}
}
I found the bug :-)
In the code snippet of the Detail Fragment ...
public void onClick(View v)
{
if(((RelativeLayout) getActivity().findViewById(R.id.relativeDescriptionInfo)).getVisibility() == View.GONE)
}
...you shouldn't use "getActivity()" use "view" from "view = inflater.inflate(R.layout.list_cachedetails, container,false);"
Then it will work

GWT - Fading in/out a background image

I have a custom class as follows which works fine, the button grows/shrinks to accomodate the text and the bg image changes on a click.
Probem I want to solve is how to "fadeIN" one or other image when clicked/notClicked is called
Here is my code
public ExpandingOvalButton(String text) {
if (text.length() > 15) {
label.getElement().getStyle().setFontSize(20, Unit.PX);
} else {
label.getElement().getStyle().setFontSize(30, Unit.PX);
}
int width = 120;
initWidget(panel);
label.setText(text);
// width = width + (text.length() * 8);
String widthStr = width + "px";
image.setWidth(widthStr);
image.setHeight("100px");
button = new PushButton(image);
button.setWidth(widthStr);
button.setHeight("50px");
panel.add(button, 0, 0);
panel.add(label, 18, 14);
}
public void isClicked()
{
image.setUrl("images/rectangle_green.png");
}
public void unClicked()
{
image.setUrl("images/rectangle_blue.png");
}
#Override
public HandlerRegistration addClickHandler(ClickHandler handler) {
return addDomHandler(handler, ClickEvent.getType());
}
public void setButtonEnabled(boolean enabled) {
// panel.setVisible(enabled);
// this.label.setVisible(enabled);
this.button.setVisible(enabled);
}
Here's a general utility class to fade any element:
public class ElementFader {
private int stepCount;
public ElementFader() {
this.stepCount = 0;
}
private void incrementStep() {
stepCount++;
}
private int getStepCount() {
return stepCount;
}
public void fade(final Element element, final float startOpacity, final float endOpacity, int totalTimeMillis) {
final int numberOfSteps = 30;
int stepLengthMillis = totalTimeMillis / numberOfSteps;
stepCount = 0;
final float deltaOpacity = (float) (endOpacity - startOpacity) / numberOfSteps;
Timer timer = new Timer() {
#Override
public void run() {
float opacity = startOpacity + (getStepCount() * deltaOpacity);
DOM.setStyleAttribute(element, "opacity", Float.toString(opacity));
incrementStep();
if (getStepCount() == numberOfSteps) {
DOM.setStyleAttribute(element, "opacity", Float.toString(endOpacity));
this.cancel();
}
}
};
timer.scheduleRepeating(stepLengthMillis);
}
}
Calling code for instance:
new ElementFader().fade(image.getElement(), 0, 1, 1000); // one-second fade-in
new ElementFader().fade(image.getElement(), 1, 0, 1000); // one-second fade-out
You could use GwtQuery. It provides fadeIn & fadeOut effects (and many other JQuery goodies), it is cross-browser compatible and seems to be pretty active.

Blackberry - Loading/Wait screen with animation

Is there a way to show "Loading" screen with animation in blackberry?
Options:
PME animation content
multithreading + set of images + timer/counter
standard rim api
some other way
Any of this?
Thanks!
Fermin, Anthony +1. Thanks to all, you gave me the part of answer.
My final solution:
1.Create or generate (free Ajax loading gif generator) animation and add it to project.
2.Create ResponseCallback interface (see Coderholic - Blackberry WebBitmapField) to receive thread execution result:
public interface ResponseCallback {
public void callback(String data);
}
3.Create a class to handle your background thread job. In my case it was http request:
public class HttpConnector
{
static public void HttpGetStream(final String fileToGet,
final ResponseCallback msgs) {
Thread t = new Thread(new Runnable() {
public void run() {
HttpConnection hc = null;
DataInputStream din = null;
try {
hc = (HttpConnection) Connector.open("http://" + fileToGet);
hc.setRequestMethod(HttpsConnection.GET);
din = hc.openDataInputStream();
ByteVector bv = new ByteVector();
int i = din.read();
while (-1 != i) {
bv.addElement((byte) i);
i = din.read();
}
final String response = new String(bv.toArray(), "UTF-8");
UiApplication.getUiApplication().invokeLater(
new Runnable() {
public void run() {
msgs.callback(response);
}
});
}
catch (final Exception e) {
UiApplication.getUiApplication().invokeLater(
new Runnable() {
public void run() {
msgs.callback("Exception (" + e.getClass() + "): "
+ e.getMessage());
}
});
}
finally {
try {
din.close();
din = null;
hc.close();
hc = null;
}
catch (Exception e) {
}
}
}
});
t.start();
}
}
4.Create WaitScreen (a hybrid of FullScreen and AnimatedGIFField with ResponseCallback interface):
public class WaitScreen extends FullScreen implements ResponseCallback
{
StartScreen startScreen;
private GIFEncodedImage _image;
private int _currentFrame;
private int _width, _height, _xPos, _yPos;
private AnimatorThread _animatorThread;
public WaitScreen(StartScreen startScreen) {
super(new VerticalFieldManager(), Field.NON_FOCUSABLE);
setBackground(
BackgroundFactory.createSolidTransparentBackground(
Color.WHITE, 100));
this.startScreen = startScreen;
EncodedImage encImg =
GIFEncodedImage.getEncodedImageResource("ajax-loader.gif");
GIFEncodedImage img = (GIFEncodedImage) encImg;
// Store the image and it's dimensions.
_image = img;
_width = img.getWidth();
_height = img.getHeight();
_xPos = (Display.getWidth() - _width) >> 1;
_yPos = (Display.getHeight() - _height) >> 1;
// Start the animation thread.
_animatorThread = new AnimatorThread(this);
_animatorThread.start();
UiApplication.getUiApplication().pushScreen(this);
}
protected void paint(Graphics graphics) {
super.paint(graphics);
// Draw the animation frame.
graphics
.drawImage(_xPos, _yPos, _image
.getFrameWidth(_currentFrame), _image
.getFrameHeight(_currentFrame), _image,
_currentFrame, 0, 0);
}
protected void onUndisplay() {
_animatorThread.stop();
}
private class AnimatorThread extends Thread {
private WaitScreen _theField;
private boolean _keepGoing = true;
private int _totalFrames, _loopCount, _totalLoops;
public AnimatorThread(WaitScreen _theScreen) {
_theField = _theScreen;
_totalFrames = _image.getFrameCount();
_totalLoops = _image.getIterations();
}
public synchronized void stop() {
_keepGoing = false;
}
public void run() {
while (_keepGoing) {
// Invalidate the field so that it is redrawn.
UiApplication.getUiApplication().invokeAndWait(
new Runnable() {
public void run() {
_theField.invalidate();
}
});
try {
// Sleep for the current frame delay before
// the next frame is drawn.
sleep(_image.getFrameDelay(_currentFrame) * 10);
} catch (InterruptedException iex) {
} // Couldn't sleep.
// Increment the frame.
++_currentFrame;
if (_currentFrame == _totalFrames) {
// Reset back to frame 0
// if we have reached the end.
_currentFrame = 0;
++_loopCount;
// Check if the animation should continue.
if (_loopCount == _totalLoops) {
_keepGoing = false;
}
}
}
}
}
public void callback(String data) {
startScreen.updateScreen(data);
UiApplication.getUiApplication().popScreen(this);
}
}
5.In the end, create Start screen to call HttpConnector.HttpGetStream and to show WaitScreen:
public class StartScreen extends MainScreen
{
public RichTextField text;
WaitScreen msgs;
public StartScreen() {
text = new RichTextField();
this.add(text);
}
protected void makeMenu(Menu menu, int instance) {
menu.add(runWait);
super.makeMenu(menu, instance);
}
MenuItem runWait = new MenuItem("wait", 1, 1) {
public void run() {
UiApplication.getUiApplication().invokeLater(
new Runnable() {
public void run() {
getFile();
}
});
}
};
public void getFile() {
msgs = new WaitScreen(this);
HttpConnector.HttpGetStream(
"stackoverflow.com/faq", msgs);
}
//you should implement this method to use callback data on the screen.
public void updateScreen(String data)
{
text.setText(data);
}
}
UPDATE: another solution naviina.eu: A Web2.0/Ajax-style loading popup in a native BlackBerry application
The basic pattern for this kind of thing is:
Have a thread running a loop that updates a variable (such as the frame index of the animated image) and then calls invalidate on a Field which draws the image (and then sleeps for a period of time). The invalidate will queue a repaint of the field.
In the field's paint method, read the variable and draw the appropriate frame of the image.
Pseudo code (not totally complete, but to give you the idea):
public class AnimatedImageField extends Field implements Runnable {
private int currentFrame;
private Bitmap[] animationFrames;
public void run() {
while(true) {
currentFrame = (currentFrame + 1) % animationFrames.length;
invalidate();
Thread.sleep(100);
}
}
protected void paint(Graphics g) {
g.drawBitmap(0, 0, imageWidth, imageHeight, animationFrames[currentFrame], 0, 0);
}
}
Note also here I used an array of Bitmaps, but EncodedImage lets you treat an animated gif as one object, and includes methods to get specific frames.
EDIT: For completeness: Add this to a PopupScreen (as in Fermin's answer) or create your own dialog by overriding Screen directly. The separate thread is necessary because the RIM API is not thread-safe: you need to do everything UI related on the event thread (or while holding the event lock, see BlackBerry UI Threading - The Very Basics
This is simple code for loading screen ....
HorizontalFieldManager popHF = new HorizontalFieldManager();
popHF.add(new CustomLabelField("Pls wait..."));
final PopupScreen waitScreen = new PopupScreen(popHF);
new Thread()
{
public void run()
{
synchronized (UiApplication.getEventLock())
{
UiApplication.getUiApplication().pushScreen(waitScreen);
}
//Here Some Network Call
synchronized (UiApplication.getEventLock())
{
UiApplication.getUiApplication().popScreen(waitScreen);
}
}
}.start();
If it's just an animation could you show an animated gif on a popup and close it when loading operation is complete?
Easiest way is probably to use the standard GaugeField, setting style GaugeField.PERCENT. This will give you a progress bar. Add this to a PopupScreen and it will sit on top of your content. Something like..
private GaugeField _gaugeField;
private PopupScreen _popup;
public ProgressBar() {
DialogFieldManager manager = new DialogFieldManager();
_popup = new PopupScreen(manager);
_gaugeField = new GaugeField(null, 0, 100, 0, GaugeField.PERCENT);
manager.addCustomField(_gaugeField);
}
Then have an update method which will use _gaugeField.setValue(newValue); to update the progress bar.
I normally have this called from whichever thread is doing the work (loading in your case, everytime an operation is complete the progress bar is updated.
I would suggest to take a look at this simple implementation. I liked this but never used it. May be helpful to you.
link text
ActivityIndicator is a good option if you are working with at least BB OS 6.0.
http://www.brighthub.com/mobile/blackberry-platform/articles/94258.aspx
http://docs.blackberry.com/en/developers/deliverables/17966/Screen_APIs_1245069_11.jsp

Resources