I have looked at the github resource and also here but I'm unable to get my graph to display live data. Here's my code.
public class Blink_HR extends Fragment {
TextView textView;
LinearLayout linearLayout;
DecoView mDecoView;
private int mBackIndex;
private int mSeries1Index;
private int mSeries2Index;
private int mSeries3Index;
private final float
mSeriesMax = 50f;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.blink_hr, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
textView = (TextView) getActivity().findViewById(R.id.meditation);
linearLayout = (LinearLayout) getView().findViewById(R.id.blinkHR);
mDecoView = (DecoView)getActivity().findViewById(R.id.dynamicArcView);
mDecoView.addEvent(new DecoEvent.Builder(mSeriesMax)
.setIndex(mBackIndex)
.setDuration(10)
.build());
SeriesItem seriesItem = new SeriesItem.Builder(Color.parseColor("#FFE2E2E2"))
.setRange(0, mSeriesMax, 0)
.setInitialVisibility(true)
.build();
mBackIndex = mDecoView.addSeries(seriesItem);
}
void update(int id, int value) {
String heart = String.valueOf(value);
Log.d("Blink Hai", heart);
if (value > 0 && mDecoView!=null && mSeries1Index!=0) {
SeriesItem seriesItem = new SeriesItem.Builder(Color.parseColor("#FFFF8800"))
.setRange(0, (float)value, 0)
.setInitialVisibility(false)
.build();
mSeries1Index = mDecoView.addSeries(seriesItem);
}
if (mDecoView != null) {
mDecoView.addEvent(new DecoEvent.Builder(42.4f)
.setIndex(mSeries1Index)
.setDelay(3250)
.build());
mDecoView.executeReset();
}
}
}
My update function is called every 1 second and I would expect the graph to update this data in real-time. However all I get is a blimp on the screen.
There is a couple of issues with the code
An event is added to the series mBackIndex before mBackIndex has been initialized
Update is triggered every 1 second but a 3.25 second delay is added to the event before it will be processed
The event on update always sets the DecoView position to 42.4
executeReset() is called every time the update is triggered, this
resets all series in the charts and cancels all pending animations
Here is some sample code that will update a DecoView every 1 second to a random position with animation
public class FauxFitActivity extends AppCompatActivity {
private DecoView mDecoView;
private int mSeries1Index;
private final float mSeriesMax = 50f;
private Handler mHandler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
update();
mHandler.postDelayed(this, 1000);
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_faux_fit);
mDecoView = (DecoView) findViewById(R.id.dynamicArcView);
createDataSeries1();
// Start the timer
mHandler.post(runnable);
}
private void createDataSeries1() {
final SeriesItem seriesItem = new SeriesItem.Builder(Color.parseColor("#FFFF8800"))
.setRange(0, mSeriesMax, 0)
.setInitialVisibility(false)
.build();
mSeries1Index = mDecoView.addSeries(seriesItem);
}
private void update() {
final Random rand = new Random();
int newPosition = rand.nextInt((int)mSeriesMax);
mDecoView.addEvent(new DecoEvent.Builder(newPosition).setIndex(mSeries1Index).setDuration(1000).build());
}
}
Related
I am designing a small application which changes a TextView every second with a random letter.
For now, my code looks like this and works very well :
#BindView(R.id.textview_letter) TextView letterTV;
private static final int DELAY_MILLIS = 1000;
private static final int LOOP_MAX = 10;
private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private Handler h = new Handler();
private Random random = new Random();
private int position = 0;
private Runnable run = new Runnable() {
#Override
public void run() {
if (position >= LOOP_MAX) {
letterTV.setText("THE END !");
h.removeCallbacks(run);
} else {
String randomLetter = String.valueOf(ALPHABET.charAt(random.nextInt(ALPHABET.length())));
letterTV.setText(randomLetter);
h.postDelayed(run, DELAY_MILLIS);
position++;
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mylayout);
ButterKnife.bind(this);
h.post(run);
}
I now want to improve it by setting the background of my TextView to black (or make it invisible) for few ms between each letter, so the user is notified in case there is twice the same letter in a row.
My only noob thought was to put another Runnable in my Runnable and handle them with split delay (like 700 and 300 ms for instance), but this seems hugely overcomplicated.
What is the right way to do this ?
(and BTW, is the Runnable/Handler really the most adapted pattern for me to use ?)
EDIT : In a non-UI thread I could do domething like this :
while (randomLetter.next()) {
letterTV.setText(randomLetter);
Thread.sleep(700);
letterTV.setBackgroundColor(Color.BLACK);
Thread.sleep(300);
}
Use RxAndroid, it will reduce the complexity:
public class Test extends AppCompatActivity {
private Disposable randomLetterObservable;
private TextView textView;
private Random random = new Random();
private final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
randomLetterObservable = Observable.interval(500L, TimeUnit.MILLISECONDS)
.timeInterval()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Timed<Long>>() {
#Override
public void accept(Timed<Long> longTimed) throws Exception {
String randomLetter = String.valueOf(ALPHABET.charAt(random.nextInt(ALPHABET.length())));
textView.setText(randomLetter);
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
if (randomLetterObservable != null) {
randomLetterObservable.dispose();
}
}
}
with dependencies:
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'io.reactivex.rxjava2:rxjava:2.1.8'
Edit: Here is a modified solution that also changes the background if the same letter occurs twice in a row.
public class Test extends AppCompatActivity {
private Disposable randomLetterObservable;
private TextView textView;
private Random random = new Random();
private final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private String lastLetter;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
randomLetterObservable = Observable.interval(700L, TimeUnit.MILLISECONDS)
.timeInterval()
.observeOn(AndroidSchedulers.mainThread())
.switchMap(new Function<Timed<Long>, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(Timed<Long> longTimed) throws Exception {
String randomLetter = String.valueOf(ALPHABET.charAt(random.nextInt(ALPHABET.length())));
if (randomLetter.equals(lastLetter)) {
textView.setBackgroundColor(Color.BLACK);
return Observable.just(randomLetter)
.observeOn(Schedulers.computation())
.debounce(300L, TimeUnit.MILLISECONDS);
} else {
lastLetter = randomLetter;
return Observable.just(randomLetter);
}
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
#Override
public void accept(String s) throws Exception {
textView.setText(s);
textView.setBackgroundColor(Color.TRANSPARENT);
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
if (randomLetterObservable != null) {
randomLetterObservable.dispose();
}
}
}
Events are emitted in a background thread, background color and letters are set on the main thread. I think this is pretty much what you asked for. (You might need to play with the values to get the desired effect).
Good luck.
My application makes a webservice call using volley, in order to update recyclerView every 10 seconds. Besides memory usage increase in 10 seconds constantly until it hits the max heap size. Then GC starts doing its job, but the memory usage does not come back down like at the beginning.
Using Eclipse MAT or Android Studio analyzer tasks, I could'nt find a single leak in my code.
I want to know that if there are suspects of leaking in my code. Any help will be appreciated.
Below I have 3 classes:
EventService send a message to MainActivity using sendBroadcast() in every 10 seconds.
MainActiviy will get message from EventService using BroadcastReceiver and calls update operation within its Fragment
EventListFragment, which is inside the MainActivity, contains a RecyclerView, that needs to be updated.
Here is my EventService:
public class EventService extends Service {
private volatile boolean isCanceled = false;
public static final String KEY_MESSAGE = "connection";
public EventService() {}
#Override
public int onStartCommand(final Intent intent, int flags, int startId) {
Runnable r = new Runnable() {
#Override
public void run() {
while (!isCanceled) {
try {
Intent i = new Intent("android.intent.action.MAIN");
AppController.getInstance().cancelPendingRequests("json_obj_req");
i.putExtra(KEY_MESSAGE, MESSAGE);
sendBroadcast(i);
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread eventThread = new Thread(r);
eventThread.start();
return Service.START_STICKY;
}
#Override
public void onDestroy() {
isCanceled = true;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Here is my MainActivity:
public class MainActivity extends AppCompatActivity {
private Intent intent;
private BroadcastReceiver mReceiver;
private EventListFragment eventListFragment;
private IntentFilter intentFilter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
private void setView() {
eventListFragment = (EventListFragment) getSupportFragmentManager().findFragmentById(R.id.frgEventList);
}
#Override
protected void onResume() {
intent = new Intent(this, EventService.class);
mReceiver = new MyReceiver(eventListFragment);
this.registerReceiver(mReceiver, intentFilter);
intent = new Intent(this, EventService.class);
startService(intent);
}
#Override
protected void onPause() {
super.onPause();
stopService(intent);
unregisterReceiver(mReceiver);
}
private static class MyReceiver extends BroadcastReceiver {
private WeakReference<EventListFragment> eventListFragment = null;
public MyReceiver(EventListFragment eventFragment) {
this.eventListFragment = new WeakReference<>(eventFragment);
}
#Override
public void onReceive(Context context, Intent intent) {
String mssg = intent.getStringExtra(KEY_MESSAGE);
EventListFragment eventFragment = eventListFragment.get();
if (mssg.equals(MESSAGE) && eventFragment != null) {
//Update recyclerView
eventFragment.eventToList();
}
}
}
}
And here is my EventListFragment:
public class EventListFragment extends Fragment {
private View view;
private RecyclerView recyclerView;
private LinearLayoutManager mLayoutManager;
private EventAdapter eventAdapter;
private RequestData requestData;
private ArrayList<EventModel> eventList;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_event_list, container, false);
return view;
}
#Override
public void onResume() {
super.onResume();
setView();
setControl();
}
private void setView() {
recyclerView = (RecyclerView) view.findViewById(R.id.frg_recycler_view);
}
private void setControl() {
if (eventAdapter == null && mLayoutManager == null) {
eventList = new ArrayList<>();
eventAdapter = new EventAdapter(getActivity().getApplicationContext(), eventList);
mLayoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setHasFixedSize(true);
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), LinearLayoutManager.VERTICAL));
recyclerView.setAdapter(eventAdapter);
}
recyclerView.addOnItemTouchListener(new RecyclerItemListener(getActivity().getApplicationContext(), recyclerView, new RecyclerItemListener.RecyclerTouchListener() {
#Override
public void onClickItem(View v, int position) {
EventModel model = eventList.get(position);
SQLiteHandler db = SQLiteHandler.getInstance(getActivity().getApplicationContext());
//some instances
}
#Override
public void onLongClickItem(View v, int position) {
}
}));
}
//make service call
public void eventToList() {
if (requestData == null) {
requestData = new RequestData(getActivity());
}
final ArrayList<EventModel> newList = new ArrayList<>(); //are you leaking?
requestData.getEventToday(new RequestData.VolleyCallback() {
#Override
public void onSuccess(JSONObject result) {
for (int i = 0; i < result.length(); i++) {
try {
JSONObject item = result.getJSONObject(Integer.toString(i));
EventModel eventModel = new EventModel();
String title = item.getString("title");
String start = item.getString("start");
String end = item.getString("end");
String date = item.getString("date");
eventModel.setDate(date);
eventModel.setStartTime(start);
eventModel.setEndTime(end);
eventModel.setTitle(title);
newList.add(eventModel);
} catch (JSONException e) {
e.printStackTrace();
}
}
eventAdapter.update(newList);
}
});
}
}
Many thanks!
First of all, a design consideration: is it necessary to call web service every 10 seconds? Do you know when/how often server data changes?
Every time you read data from web server, application have to do a lot of work: you create many object, update the adapter etc. Moreover, think about network traffic, you use network every 10 seconds.
There are somethings you can do:
Increment wait time: in this way, you reduce the number of created object/per seconds.
Reduce local reference for temporary objects (see following code)
Check if recycler view's adapter, before add new values, the old ones was correctly deferred.
Evaluate if it is possible to use technology to push data, you to avoid data polling. You can see GCM.
For consideration #2, i try rewrite eventToList method:
public void eventToList() {
if (requestData == null) {
requestData = new RequestData(getActivity());
}
requestData.getEventToday(new RequestData.VolleyCallback() {
#Override
public void onSuccess(JSONObject result) {
ArrayList<EventModel> newList = new ArrayList<>();
JSONObject item;
EventModel eventModel;
String title;
String start;
String end;
String date;
for (int i = 0; i < result.length(); i++) {
try {
item = result.getJSONObject(Integer.toString(i));
eventModel = new EventModel();
title = item.getString("title");
start = item.getString("start");
end = item.getString("end");
date = item.getString("date");
eventModel.setDate(date);
eventModel.setStartTime(start);
eventModel.setEndTime(end);
eventModel.setTitle(title);
newList.add(eventModel);
} catch (JSONException e) {
e.printStackTrace();
}
}
eventAdapter.update(newList);
}
});
}
I am working on a simple game with graphics using Open GL. I want it so that when a certain event happens, the player gets a point, and this updates a textview overlaying the GLSurfaceView on the game activity.
Thanks in advance!
Here are the relevant parts of my code:
The Activity:
public class playActivity extends Activity {
private GLSurfaceView myGLView;
private Handler mGameHandler;
private TextView mScoreView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mScoreView = new TextView(this);
mScoreView.setTextColor(0xFF00FF00); // Green
string myZeroString = "0";
mScoreView.setText(myZeroString); //Starting score will always be 0
mGameHandler = new Handler(){
public void handleMessage(Message msg){
if (msg.what == MyGLRenderer.GAME_SCORE_FLAG) {
int score = msg.arg1;
mScoreView.setText(Integer.toString(score));
}
}
};
myGLView = new MyGLSurfaceView(this);
setContentView(myGLView);
addContentView(mScoreView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
private void updateScoreBoard(int score){
mScoreView.setText(Integer.toString(score));
}
class MyGLSurfaceView extends GLSurfaceView{
private final MyGLRenderer myRenderer;
public MyGLSurfaceView(Context context){
super(context);
// Create an OpenGL ES 2.0 context
setEGLContextClientVersion(2);
myRenderer = new MyGLRenderer(context);
myRenderer.initiateHandler(mGameHandler);
// Set the Renderer for drawing on the GLSurfaceView
setRenderer(myRenderer);
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
}
}
And the GlSurfaceView.Renderer:
public class MyGLRenderer implements GLSurfaceView.Renderer{
public static final int GAME_SCORE_FLAG = 1;
private Handler mGameHandler = null;
private int mScore = 0;
public void initiateHandler(Handler handler){
mGameHandler = handler;
}
...
#Override
public void onDrawFrame(GL10 unused){
if (scoreCondition){
mScore += 1;
if (mGameHandler != null) {
int flag = MyGLRenderer.GAME_SCORE_FLAG;
mGameHandler.dispatchMessage(Message.obtain(mGameHandler, flag, 2));
}
}
}
}
When I run this, it gives me the error:
"android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views."
However, in the top chunk of code, when I replace
int score = msg.arg1;
mScoreView.setText(Integer.toString(score));
with
final int score = 3;
runOnUiThread(new Runnable() {
#Override
public void run() {
mScoreView.setText(Integer.toString(score));
}
});
I no longer get the error and it works. I thought that in the first construction that when I called setText that it would have to be performed on the UI thread, but Android Studio thinks differently. Can anyone explain this to me? Thanks!
Change:
mGameHandler.dispatchMessage(Message.obtain(mGameHandler, flag, 2));
to:
mGameHandler.sendMessage(Message.obtain(mGameHandler, flag, 2));
Calling dispatchMessage() will cause the message to be delivered on the current thread (which is the GL thread in your case.)
When the screen rotates my seekbar's colored bar goes back to its initial value, while the thumb remains at the correct position.
Basically from this:
It becomes like this:
Note that the TextView showing 15 is connected to the seekbar and correctly shows the same value, which is updated in onCreateView retrieving the value with getProgress on the seekbar, so the seekbar has the correct progress internally but "forgets" to update its bar. Note also that if moved slightly, the bar will be updated correctly.
The strange thing is that I have an identical seekbar, on which I do exactly the same actions(method calls etc) but this one never has this problem.
They are defined in the same way in the XML layout file(except for the id).
These seekbars are inside a Fragment shown into a ViewPager, here's more or less the code for the fragment:
public class NewCharacterNameFragment extends Fragment
implements NumericSeekBar.OnValueChangedListener {
private static final String LEVEL = "org.my.package.LEVEL";
private static final String STATS = "org.my.package.STATS";
private NumericSeekBar levelBar; // Causing problems
private NumericSeekBar statsBar; // Well behaved
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (savedInstanceState == null) { // defaults values to avoid multiple checks later
savedInstanceState = new Bundle();
savedInstanceState.putInt(LEVEL, 1);
savedInstanceState.putInt(STATS, 30);
}
View view = inflater.inflate(R.layout.new_character_name_fragment, container,
false);
levelBar = (NumericSeekBar) view.findViewById(R.id.levelSeekBar);
statsBar = (NumericSeekBar) view.findViewById(R.id.statPointsSeekBar);
levelBar.setValue(savedInstanceState.getInt(LEVEL));
levelBar.setMax(20);
levelBar.setValueChangedListener(this);
statsBar.setValue(savedInstanceState.getInt(STATS));
statsBar.setMax(100);
statsBar.setValueChangedListener(this);
// Initialize the text-views with the progress values:
TextView tView = (TextView) view.findViewById(R.id.statPointsNumTextView);
tView.setText(Integer.valueOf(statsBar.getValue()).toString());
tView = (TextView) view.findViewById(R.id.levelNumTextView);
tView.setText(Integer.valueOf(levelBar.getValue()).toString());
return view;
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putInt(LEVEL, levelBar.getValue());
savedInstanceState.putInt(STATS, statsBar.getValue());
}
#Override
public void onNumericSeekBarValueChanged(NumericSeekBar bar, int value,
boolean fromUser) {
// Called whenever the seekbar value changes
if (bar == statsBar) {
TextView view = (TextView) getView().findViewById(R.id.statPointsNumTextView);
view.setText(Integer.valueOf(value).toString());
} else if (bar == levelBar) {
TextView view = (TextView) getView().findViewById(R.id.levelNumTextView);
view.setText(Integer.valueOf(value).toString());
}
}
}
Where NumericSeekBar is a widget I created and is basically a LinearLayout with the two increment and decrement buttons and the seekbar:
public class NumericSeekBar extends LinearLayout
implements SeekBar.OnSeekBarChangeListener, View.OnClickListener {
public interface OnValueChangedListener {
public void onNumericSeekBarValueChanged(NumericSeekBar bar, int value,
boolean fromUser);
}
private Button incButton;
private Button decButton;
private SeekBar seekBar;
private OnValueChangedListener listener = null;
private int maxValue = 100;
private int value = 0;
public NumericSeekBar(Context ctx) {
super(ctx);
setOpts();
setWidgets();
}
public NumericSeekBar(Context ctx, AttributeSet attributes) {
super(ctx, attributes);
setOpts();
setWidgets();
}
public void setValueChangedListener(OnValueChangedListener listener) {
this.listener = listener;
}
public void setMax(int maxValue) {
this.maxValue = maxValue;
seekBar.setMax(maxValue);
}
public int getValue() {
return this.value; // using seekBar.getProgress() obtains same results
}
public boolean setValue(int value) {
if (value < 0 || value > maxValue) {
return false;
}
this.value = value;
seekBar.setProgress(value);
return true;
}
#Override
public void onStopTrackingTouch(SeekBar bar) {
bar.setSecondaryProgress(bar.getProgress());
}
#Override
public void onStartTrackingTouch(SeekBar bar) {
}
#Override
public void onProgressChanged(SeekBar bar, int value, boolean fromUser) {
this.value = value;
if (listener != null ){
listener.onNumericSeekBarValueChanged(this, value, fromUser);
}
if (!fromUser) {
bar.setSecondaryProgress(0);
}
}
#Override
public void onClick(View v) {
// Handle increment/decrement button clicks
if (v.equals(incButton)) {
this.setValue(this.getValue() + 1);
} else if(v.equals(decButton)) {
this.setValue(this.getValue() - 1);
}
}
private void setOpts() {
setOrientation(HORIZONTAL);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setShowDividers(SHOW_DIVIDER_NONE);
}
}
private void setWidgets() {
incButton = new Button(getContext());
decButton = new Button(getContext());
seekBar = new SeekBar(getContext());
incButton.setText("+");
incButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
incButton.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
(float) 0.4));
incButton.setOnClickListener(this);
decButton.setText("-");
decButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
decButton.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
(float) 0.4));
decButton.setOnClickListener(this);
LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
(float) 0.2);
layoutParams.gravity = Gravity.CENTER;
seekBar.setLayoutParams(layoutParams);
setMax(this.maxValue);
setValue(this.value);
addView(incButton);
addView(seekBar);
addView(decButton);
seekBar.setOnSeekBarChangeListener(this);
}
}
This happens both on the emulator and on a physical device.
EDIT: I just tested my app on an android 4 emulator and this does not happen, so it seems to be something 2.x related.
EDIT2: I've tried to invalidate() the bars in onCreateView, onStart and onResume but the problems still occurs. I've also tried to put a levelBar.setValue(levelBar.getValue()) in onResume but nothing changed.
I really don't understand what's happening.
EDIT3: I've added an other fragment which contains six of these bars and only the levelBar in the code above behaves strangely. I wonder how is this possible. Either there is some really strange bug, or I'm doing something not properly, even though I can't see where(and in android 4.x all works well).
EDIT4: My third edit is incorrect: now almost all the bars have this behaviour. The statsBar above seems to be the only one that is never affected.
I finally understood what's wrong with the code.
It seems like changing the maximum value of a SeekBar does not trigger a repaint of the color bar while it does trigger a repaint of the thumb. This is probably a bug in android 2.x(since it does not happen in android 4.x).
To solve this problem you simply have to set the maximum value before setting the progress on the seek-bar.
In my case only some bars were affected because I set the default maximum for the NumericSeekBar to 100, and only the bars with a different maximum where affected.
It's still not clear why invalidating the view in onResume does not produce the correct re-drawn of the widget.
I find the recurrence method:
final SeekBar seekBar = (SeekBar) findViewById(R.id.test_seekbar);
seekBar.setMax(4000);
seekBar.setProgress(1500);
new Thread(new Runnable() {
public void run() {
seekBar.setProgress(500);
}
}).start();
for (int i = 0; i < 5000; i++) {
seekBar.setProgress(2000);
}
above code, called ProgressBar#setProgress in work thread, it causes the bug.
Android official documentation advice called it at main thread, like this:
public class MyActivity extends Activity {
private static final int PROGRESS = 0x1;
private ProgressBar mProgress;
private int mProgressStatus = 0;
private Handler mHandler = new Handler();
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.progressbar_activity);
mProgress = (ProgressBar) findViewById(R.id.progress_bar);
// Start lengthy operation in a background thread
new Thread(new Runnable() {
public void run() {
while (mProgressStatus < 100) {
mProgressStatus = doWork();
// Update the progress bar
mHandler.post(new Runnable() {
public void run() {
mProgress.setProgress(mProgressStatus);
}
});
}
}
}).start();
}
}
Here is a class that works perfectly until I wanted to add one more feature, The code compiles then crashes immediately upon execution. The problem lies with the componentAnalyzer class that I wish to implement in this class. I donĀ“t know why it won't work because I implemented this componentAnalyzer in another class in the exact same way and it works beautifully.
I think its a small mistake but unsure. I commented out the part that was creating problems because the rest works and should not be touched.
The method that will use the componentAnalyzer is at the end of the code. I cut out everything that was working in order to see it easier.
public class PowerMonitorActivity extends Activity implements SeekBar.OnSeekBarChangeListener {
private static boolean instantiated = false;
//private static ComponentAnalyzer componentAnalyzer;
//private Context context;
private Button changeGPSButton;
private Button changeAudioButton;
private Button locationUpdateButton;
private SeekBar brightnessSeekBar;
private int screenBrightness = 123;
private Handler cpuHandler = new Handler();
private CPUMonitor cpuMonitor = new CPUMonitor();
private int updateTime = 1000;
private Handler totalPowerHandler = new Handler ();
private MediaManager mediaManager = new MediaManager();
private boolean requestingLocation = false;
private boolean wifiIsTransmitting = false;
private boolean wifiIsConnected = false;
private boolean cellIsTransmitting = false;
private boolean cellIsConnected = false;
private boolean isLogging = false;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Bundle extras = getIntent().getExtras();
if (!instantiated) {
instantiated = true;
//componentAnalyzer = new ComponentAnalyzer(context, extras);
}
// Create GPS button - note that location settings cannot be changed directly
// in a program. Rather, a settings screen is shown when this button is pressed.
changeGPSButton = (Button)findViewById(R.id.changeGPS);
changeGPSButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
showGPSOptions();
}
});
totalPowerHandler.removeCallbacks(updatePower);
totalPowerHandler.postDelayed(updatePower, 1000);
}
private Runnable updateCpu = new Runnable() {
public void run() {
float util = cpuMonitor.getUtil();
int rutil = Math.round(100*util);
TextView cpuTextView = (TextView)findViewById(R.id.cpuTextView);
cpuTextView.setText(rutil+"%");
cpuHandler.postDelayed(this, updateTime);
}
};
private Runnable updatePower = new Runnable()
{
public void run()
{
//float testPower = componentAnalyzer.getWifiPower();;
TextView totalPowerView = (TextView)findViewById(R.id.totalPowerTextView);
//totalPowerView.setText(testPower+"mW");
totalPowerHandler.postDelayed(this, 1000);
}
};
Did you initialize context? In the current code example context is null when the ComponentAnalyzer is instantiated.
For my point of view, your application context is null (you forgot to assign reference to context for current activity or application)
Remove comments for problematic lines,
Now look at these lines,
Bundle extras = getIntent().getExtras();
if (!instantiated) {
instantiated = true;
componentAnalyzer = new ComponentAnalyzer(context, extras); // Here context is null
}
change this according to given below,
Bundle extras = getIntent().getExtras();
if (!instantiated) {
instantiated = true;
if(extras != null)
componentAnalyzer = new ComponentAnalyzer(PowerMonitorActivity.this, extras);
else Log.e("PowerMonitorActivity","Bundle extras is null");
}