I'm attempting to make a simple widget that displays the battery percentage both textually and graphically in a widget. The textual part works without problem, but I'm having great difficulty getting the widget to graphically update.
Graphically, I have a battery image that I clip according to battery percentage. I'm attempting to use ClipDrawable for this.
battery_widget_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/widgetLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:padding="#dimen/widget_padding"
android:background="#drawable/battery_clip_layer" >
<TextView
android:id="#+id/batteryPercentageWidgetTextView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="#string/battery_percentage_widget_default"
android:gravity="center" />
</LinearLayout>
battery_clip_layer.xml (ie a ClipDrawable)
<clip xmlns:android="http://schemas.android.com/apk/res/android"
android:clipOrientation="vertical"
android:gravity="bottom"
android:drawable="#drawable/battery_shape" />
BatteryService.java - a service that receives battery events and updates the widget via a RemoteViews
public class BatteryService extends Service {
private static final String LOG = BatteryService.class.getName();
private final AtomicInteger batteryPercentage = new AtomicInteger(100);
private final BroadcastReceiver batteryUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int level = intent.getIntExtra("level", 0);
batteryPercentage.set(level);
updateWidget();
}
};
#Override
public void onCreate() {
super.onCreate();
registerReceiver(batteryUpdateReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Log.i(LOG, "Created...");
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(batteryUpdateReceiver);
Log.i(LOG, "Destroyed...");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(LOG, "Started...");
updateWidget();
return super.onStartCommand(intent, flags, startId);
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
private void updateWidget() {
final int percentage = batteryPercentage.get();
log.i(LOG, "Updated... " + percentage);
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.battery_widget_layout);
remoteViews.setTextViewText(R.id.batteryPercentageWidgetTextView, percentage + "%");
//ATTEMPT 1 - no cigar
remoteViews.setInt(R.drawable.battery_clip_layer, "setLevel", 5000);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
appWidgetManager.updateAppWidget(new ComponentName(this, BatteryAppWidgetProvider.class), remoteViews);
}
private void updateWidgetAttempt2() {
final int percentage = batteryPercentage.get();
Log.i(LOG, "Updated... " + percentage);
//ATTEMPT 2 - still no cigar
Drawable clipLayer = getApplicationContext().getResources().getDrawable(R.drawable.battery_clip_layer);
if (clipLayer instanceof ClipDrawable) {
ClipDrawable clipDrawable = (ClipDrawable) clipLayer;
int level = clipDrawable.getLevel();
if (clipDrawable.setLevel(10000)) {
clipDrawable.invalidateSelf();
}
Log.i(LOG, "Updated clip amount..." + level + " -> " + ((ClipDrawable) clipLayer).getLevel());
}
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.battery_widget_layout);
remoteViews.setTextViewText(R.id.batteryPercentageWidgetTextView, percentage + "%");
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
appWidgetManager.updateAppWidget(new ComponentName(this, BatteryAppWidgetProvider.class), remoteViews);
}
}
In BatteryService please have a look at the two different attempts to update the widget (updateWidget() and updateWidgetAttempt2()). Neither attempt is successful.
I feel that I'm doing something fundamentally wrong. I'm most grateful for any help/advice! :)
You are correct in that you need to set the level on a Drawable instance, but unfortunately you can't get at the Drawable because it's in a remote view.
However, ImageView does have a method called setImageLevel, which you can call remotely. My suggestion would be to put the Drawable in an ImageView instead of as the background of the container, and then use setInt(R.id.image_view, "setImageLevel", level).
Here's what the layout should look like:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/widgetLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="#dimen/widget_padding" >
<ImageView
android:id="#+id/battery"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/battery_clip_layer" />
<TextView
android:id="#+id/batteryPercentageWidgetTextView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="#string/battery_percentage_widget_default"
android:gravity="center" />
</FrameLayout>
And the updateWidget() method:
private void updateWidget() {
final int percentage = batteryPercentage.get();
log.i(LOG, "Updated... " + percentage);
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.battery_widget_layout);
remoteViews.setTextViewText(R.id.batteryPercentageWidgetTextView, percentage + "%");
remoteViews.setInt(R.id.battery, "setImageLevel", percentage * 100);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
appWidgetManager.updateAppWidget(new ComponentName(this, BatteryAppWidgetProvider.class), remoteViews);
}
The two attempts you made regarding using a ClipDrawable in a widget are incorrect. In the first attempt you use the setInt() method incorrectly as that method expects the first int parameter to be the id of a View from the widget's layout(and not a drawable reference like you try), also there isn't a setLevel() method on any View subclass from the Android SDK, that method is present in the Drawable class. Your second approach is also incorrect as all you do is load a Drawable, set its level and that's all, that newly created Drawable has no relation to the widget's LinearLayout root.
To make the ClipDrawable work you'll need to set its level to the desired value while the drawable is already set as the LinearLayout's background or when you have the possibility of setting it as the background. Unfortunately I don't see how you could do this when the layout is used in a widget.
You'll need to use a workaround(Jschools provided one).
Related
So I've got this problem in my custom view. I'm trying to create a custom RelativeLayout with an infinite scrolling animation. To achieve this, I've created a layout backgroundscrollrelativelayout.xml like so:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/mainTile"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/topTile" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/leftTile" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/diagonalTile" />
</RelativeLayout>
The idea is that the ImageViews will translate their position on an animation update callback.
I've created the BackgroundScrollRelativeLayout.java like so:
public class BackgroundScrollRelativeLayout extends RelativeLayout {
final int layoutToUse = R.layout.backgroundscrollrelativelayout;
final int mainTileId = R.id.mainTile;
final int leftTileId = R.id.leftTile;
final int topTileId = R.id.topTile;
final int diagonalTileId = R.id.diagonalTile;
final float valueStart = 0.0f;
final float valueEnd = 1.0f;
final long animationDuration = 50000L;
private Context mContext;
private ValueAnimator scrollAnimator;
private ImageView mainTile;
private ImageView leftTile;
private ImageView topTile;
private ImageView diagonalTile;
public BackgroundScrollRelativeLayout(Context context) {
super(context);
mContext = context;
acquireViewsInLayout();
initializeAnimator();
scrollAnimator.start();
}
public BackgroundScrollRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
acquireViewsInLayout();
initializeAnimator();
scrollAnimator.start();
}
public BackgroundScrollRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
acquireViewsInLayout();
initializeAnimator();
scrollAnimator.start();
}
#Override
public void setBackgroundColor(int color) {
// Not supported
}
#Override
public void setBackgroundResource(int resid) {
// Not supported
}
#Override
public void setBackground(Drawable background) {
mainTile.setBackground(background);
leftTile.setBackground(background);
}
/*
#Override
public void setBackgroundDrawable(Drawable background) {
//super.setBackgroundDrawable(background);
//mainTile.setBackground(background);
//leftTile.setBackground(background);
}*/
// Intent: Inflate the layout associated with this View
private void inflateLayout(){
// TO inflateLayout, we connect the inflater to context, then we call inflate the layout associated
// with this view
//LayoutInflater inflater = LayoutInflater.from(mContext);
//inflater.inflate(layoutToUse, this);
inflate(getContext(), layoutToUse, this);
}
// Intent: Find all Views in Layout
private void findAllViewsById(){
mainTile = (ImageView)this.findViewById(mainTileId);
leftTile = (ImageView)this.findViewById(leftTileId);
topTile = (ImageView)this.findViewById(topTileId);
diagonalTile = (ImageView)this.findViewById(diagonalTileId);
}
// Intent: Concretely acquire all Views in Layout
private void acquireViewsInLayout(){
// TO acquireViewsInLayout, we inflate the layout,
// then we find the view of each known view id and save the view
inflateLayout();
findAllViewsById();
}
// Intent: Initialize animator properties
private void initializeAnimator(){
// TO initializeAnimator, we set how the animator will keep track of animation,
// then we set the animation repeat type, then we set the type of interpolation,
// then we set the animation duration, then we apply animation update listener
scrollAnimator = ValueAnimator.ofFloat(valueStart, valueEnd);
scrollAnimator.setRepeatCount(ValueAnimator.INFINITE);
scrollAnimator.setInterpolator(new LinearInterpolator());
scrollAnimator.setDuration(animationDuration);
addScrollAnimatorUpdateListener();
}
// Intent: Add an update listener to the scroll animator
private void addScrollAnimatorUpdateListener(){
// TO addScrollAnimatorUpdateListener, we add an update listener to scroll animator
scrollAnimator.addUpdateListener( new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
// Do something...
updateScrollAnimation();
}
});
}
private void updateScrollAnimation(){
float progress = (float)scrollAnimator.getAnimatedValue();
float widthOfTile = mainTile.getWidth();
float moveInXAxis = widthOfTile * progress;
mainTile.setTranslationX(moveInXAxis);
leftTile.setTranslationX(moveInXAxis - widthOfTile);
// Ignore the rest for now
topTile.setTranslationY(-1.0f);
diagonalTile.setTranslationX(-1.0f);
diagonalTile.setTranslationY(-1.0f);
}
}
I use my custom view like so in an activity:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.martianstudio.adivinaque.AppSettingsActivity">
<com.martianstudio.adivinaque.BackgroundScrollRelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/main_activity_animation_icon" />
</RelativeLayout>
The goal of this is to allowing the user to specify the background that they want to scroll with android:background. I do not want the parent RelativeLayout to take on this drawable as a background. Instead, I want the ImageViews to set the drawable as the background. I have overridden the setBackground in my custom view so that only the ImageViews set the drawable as the background and not the RelativeLayout (as it would by default).
#Override
public void setBackground(Drawable background) {
mainTile.setBackground(background);
leftTile.setBackground(background);
}
However, I get this error:
java.lang.NullPointerException at com.martianstudio.adivinaque.BackgroundScrollRelativeLayout.setBackground
This says that mainTile in setBackground has not been set (it is null). In my findAllViewsById() I explicitly write this.mainTile = (ImageView)this.findViewById(mainTileId);, where mainTileId = R.id.mainTile, and I inflate the layout in inflateLayout(). Both of these methods are called in the constructors of the class. It seems to me that for some reason, it cannot find the ImageView with the mainTile Id when I override the setBackground method like I do. If I don't override the setBackground method, I do not get a NullPointerException, however, it defaults to the default setBackground method, which is what I do not want. Am I missing somthing?
Any help will be appreciated :). Thank you!
What i am doing : I am trying to play an mp3 file from an url in android. and control its position through progressbar.
what is happening:
FOR the strings.xml below code is working correctly song is playing and i am able to position the progress using the progressbar
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Android mp3 player</string>
<string name="testsong_20_sec">http://users.skynet.be/fa046054/home/P22/track06.mp3</string>
FOR the strings.xml below code the code is not working properly, i mean song is playing but i cannot control the position using the progress bar. only difference with this url is that this mp3 file is in google drive.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Android mp3 player</string>
<string name="testsong_20_sec">https://286898c83aa5e6a4f7b29cdbef3b2f54430b4e71.googledrive.com/host/0B6a-NfRKFX8HQVZRSzFON1ExZEE/Chadh%20Gayi%20Maa%20Tere%20Naam%20Ki%20Sherewali%20Ka%20Sancha.mp3</string>
</resources>
What i am looking for: how to make sure my code plays proper music and i could toggle using progress bar in the second url properly
StreamingMp3Player.java
public class StreamingMp3Player extends Activity implements OnClickListener, OnTouchListener, OnCompletionListener, OnBufferingUpdateListener{
private ImageButton buttonPlayPause;
private SeekBar seekBarProgress;
public EditText editTextSongURL;
private MediaPlayer mediaPlayer;
private int mediaFileLengthInMilliseconds; // this value contains the song duration in milliseconds. Look at getDuration() method in MediaPlayer class
private final Handler handler = new Handler();
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initView();
}
/** This method initialise all the views in project*/
private void initView() {
buttonPlayPause = (ImageButton)findViewById(R.id.ButtonTestPlayPause);
buttonPlayPause.setOnClickListener(this);
seekBarProgress = (SeekBar)findViewById(R.id.SeekBarTestPlay);
seekBarProgress.setMax(99); // It means 100% .0-99
seekBarProgress.setOnTouchListener(this);
editTextSongURL = (EditText)findViewById(R.id.EditTextSongURL);
editTextSongURL.setText(R.string.testsong_20_sec);
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnCompletionListener(this);
}
/** Method which updates the SeekBar primary progress by current song playing position*/
private void primarySeekBarProgressUpdater() {
seekBarProgress.setProgress((int)(((float)mediaPlayer.getCurrentPosition()/mediaFileLengthInMilliseconds)*100)); // This math construction give a percentage of "was playing"/"song length"
if (mediaPlayer.isPlaying()) {
Runnable notification = new Runnable() {
public void run() {
primarySeekBarProgressUpdater();
}
};
handler.postDelayed(notification,1000);
}
}
#Override
public void onClick(View v) {
if(v.getId() == R.id.ButtonTestPlayPause){
/** ImageButton onClick event handler. Method which start/pause mediaplayer playing */
try {
mediaPlayer.setDataSource(editTextSongURL.getText().toString()); // setup song from http://www.hrupin.com/wp-content/uploads/mp3/testsong_20_sec.mp3 URL to mediaplayer data source
mediaPlayer.prepare(); // you must call this method after setup the datasource in setDataSource method. After calling prepare() the instance of MediaPlayer starts load data from URL to internal buffer.
} catch (Exception e) {
e.printStackTrace();
}
mediaFileLengthInMilliseconds = mediaPlayer.getDuration(); // gets the song length in milliseconds from URL
if(!mediaPlayer.isPlaying()){
mediaPlayer.start();
buttonPlayPause.setImageResource(R.drawable.button_pause);
}else {
mediaPlayer.pause();
buttonPlayPause.setImageResource(R.drawable.button_play);
}
primarySeekBarProgressUpdater();
}
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if(v.getId() == R.id.SeekBarTestPlay){
/** Seekbar onTouch event handler. Method which seeks MediaPlayer to seekBar primary progress position*/
if(mediaPlayer.isPlaying()){
SeekBar sb = (SeekBar)v;
int playPositionInMillisecconds = (mediaFileLengthInMilliseconds / 100) * sb.getProgress();
mediaPlayer.seekTo(playPositionInMillisecconds);
}
}
return false;
}
#Override
public void onCompletion(MediaPlayer mp) {
/** MediaPlayer onCompletion event handler. Method which calls then song playing is complete*/
buttonPlayPause.setImageResource(R.drawable.button_play);
}
#Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
/** Method which updates the SeekBar secondary progress by current song loading from URL position*/
seekBarProgress.setSecondaryProgress(percent);
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/widget31"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<EditText
android:id="#+id/EditTextSongURL"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:ems="10"
android:height="100dp"
android:lines="3"
android:maxLines="3"
android:minLines="1" >
<requestFocus />
<requestFocus />
</EditText>
<ImageButton
android:id="#+id/ButtonTestPlayPause"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/EditTextSongURL"
android:layout_centerHorizontal="true"
android:contentDescription="TestPlayPause"
android:onClick="onClick"
android:src="#drawable/button_play" />
<SeekBar
android:id="#+id/SeekBarTestPlay"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="#+id/ButtonTestPlayPause" />
</RelativeLayout>
This happens because google drive HTTP server does not return proper length of the file.
$ wget -S https://286898c83aa5e6a4f7b29cdbef3b2f54430b4e71.googledrive.com/host/0B6a-NfRKFX8HQVZRSzFON1ExZEE/Chadh%20Gayi%20Maa%20Tere%20Naam%20Ki%20Sherewali%20Ka%20Sancha.mp3
...
Length: unspecified [audio/mpeg]
You have no total stream length, and therefore you don't have position relative to total length. Bad luck.
I'm writing a simple UI activity, the intention of which is to create an offscreen OpenGL surface and have a GL test render into it in a background GL thread. The rendering should start at a click of the button and the TextView should display some environment information (OpenGL version, etc.) as well as the test progress (as it may take a while) and result. In the sample I'm providing I have a 5 second delay in the 'run' function of the Runnable, but in the actual app code I have a JNI call to a native library that has the GL rendering code.
I've verified that the problem I'm experiencing exists with the 5 second delay as well as the original JNI call. The problem is that while the test is running (a 5 second delay in this sample), UI seems to be frozen and does not update until the test is done. The text that I'm setting to my TextView only appears when the test is finished. I thought that "queueEvent(new TestRunnable());" starts the runnable in a separate GL thread so that UI thread can continue with no interruptions, but it does not seem to be the case, or something else is missing. Do you guys/gals have any idea what I'm doing wrong?
The simplified layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:keepScreenOn="true" android:orientation="vertical"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<Button
android:id="#+id/startTestButton" android:textSize="18sp"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:text="#string/startTestButton" android:onClick="onStartTestClick"/>
<ScrollView android:layout_width="wrap_content" android:layout_height="wrap_content">
<TextView
android:id="#+id/logView" android:textSize="18sp"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</ScrollView>
<com.company.name.MyView
android:id="#+id/myView"
android:layout_width="match_parent" android:layout_height="match_parent"/>
</LinearLayout>
The simplified activity class is defined as follows:
public class MyActivity extends Activity
{
private String m_LogText = null;
private Button m_StartTestButton = null;
private TextView m_TestLog = null;
private MyView m_SurfaceView;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
m_StartTestButton = (Button) findViewById(R.id.startTestButton);
m_SurfaceView = (MyView) findViewById(R.id.myView);
m_TestLog = (TextView) findViewById(R.id.logView);
// Check for OpenGL 2.0 support.
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo info = am.getDeviceConfigurationInfo();
m_LogText = "Open GLES version detected: " + Integer.toHexString(info.reqGlEsVersion) + "\n";
m_TestLog.setText(m_LogText);
}
public void onStartTestClick(View view)
{
//
// This text here does not appear until the end of the test:
//
m_LogText += "\nStarting the test: default\n";
m_LogText += "Test is running, please wait...\n";
m_TestLog.setText(m_LogText);
m_SurfaceView.StartTest();
}
}
Simplified MyView class is defined as follows:
public class MyView extends GLSurfaceView
{
private TestRenderer m_Renderer = null;
private class TestRenderer implements GLSurfaceView.Renderer
{
public void onDrawFrame(GL10 gl) { }
public void onSurfaceChanged(GL10 gl, int width, int height) { }
public void onSurfaceCreated(GL10 gl, EGLConfig config) { }
class TestRunnable implements Runnable
{
public void run()
{
try
{
Thread.sleep(5000);
}
catch(InterruptedException ex)
{
Thread.currentThread().interrupt();
}
}
}
public void StartTest()
{
queueEvent(new TestRunnable());
}
}
public MyView(Context context)
{
this(context, null);
}
public MyView(Context context, AttributeSet attrs)
{
super(context, attrs);
m_Renderer = new TestRenderer();
setRenderer(m_Renderer);
}
public void StartTest()
{
m_Renderer.StartTest();
}
}
I need to create Collapse / Expand forms in Android. I am thinking about using either RelativeLayout or TableLayout for this purpose. But, what XML element make these forms expand and hide in android?
If you are not sure what I am not talking about, take an application like Sales Force for an example. There you have these expandable menus in all the forms. How can I do this?
Following is an example (taken from Sales Force)
When you expand these, it looks like below
You could do the following. create a layout that has the following:
1. A Heading or a textview with the label contacts
2. Below it a layout that has forms related to it
3. Add another textview below #2 and name it address
4. Add a lyout below #3 .
The layout 2 and 4 will have visibility gone in the first case
When the user taps on layout 1, or the first textview, make layout 2 visible and vice versa. Do the same with the second textview.
Hope that helps.!
I have had a similar problem, i want parts of my form to be hidden in sektions and created a class for this issue.
public class section extends LinearLayout{
public LinearLayout container;
public Button toggler;
public section(Context context, String section_name, String section_state) {
super(context);
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.flxsection, this);
container = (LinearLayout) this.findViewById(R.id.container);
container.setVisibility(section_state.equals("0") ? View.GONE:View.VISIBLE);
toggler = ((Button)this.findViewById(R.id.section_toggle));
toggler.setTag(section_state);
toggler.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
String tag = (String) v.getTag();
v.setTag(tag.equals("0") ? "1":"0");
if(tag.equals("0")){expand(container,false);}else{collapse(container,false);}
setImage(tag.equals("0"));
}
});
toggler.setText(" " + section_name);
setImage(section_state.equals("1"));
setTextSize();
}
public void setTextSize(){
toggler.setTextSize(GV.Style.TextSize);
}
public void setImage(boolean open){
int a = open ? R.drawable.minus_48_white: R.drawable.plus_48_white;
Drawable img = main.res.getDrawable(a);
final float scale = main.res.getDisplayMetrics().density;
int size = (int) (12 * scale + 0.5f);
img.setBounds(0,0,size,size);
toggler.setCompoundDrawables(img,null,null,null);
}
}
the xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:focusable="false"
android:layout_marginLeft="4dip"
android:layout_marginRight="4dip"
android:orientation="vertical" >
<Button
android:id="#+id/section_toggle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dip"
android:layout_marginTop="4dip"
android:background="#drawable/section"
android:drawableLeft="#drawable/plus_48"
android:focusable="false"
android:gravity="left|center_vertical"
android:padding="6dip"
android:textAppearance="?android:attr/textAppearanceLargeInverse"
android:textSize="22dip" />
<LinearLayout
android:id="#+id/container"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:background="#android:color/transparent"
android:orientation="vertical" >
</LinearLayout>
Expand and collapse:
public static void expand(final View v,boolean quick) {
v.requestLayout();
v.measure(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
final int targtetHeight = v.getMeasuredHeight();
v.getLayoutParams().height = 0;
v.getLayoutParams().width = LayoutParams.FILL_PARENT;
v.setVisibility(View.VISIBLE);
if(quick){
v.getLayoutParams().height = LayoutParams.WRAP_CONTENT;
v.requestLayout();
}else{
android.view.animation.Animation a = new android.view.animation.Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
v.getLayoutParams().height = interpolatedTime == 1
? LayoutParams.WRAP_CONTENT
: (int)(targtetHeight * interpolatedTime);
v.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
// 1dp/ms
int duration = (int)(targtetHeight / v.getContext().getResources().getDisplayMetrics().density);
if(duration> 500)duration=500;
a.setDuration(duration);
//(int)(targtetHeight / v.getContext().getResources().getDisplayMetrics().density)
v.startAnimation(a);
}
}
public static void collapse(final View v,boolean quick) {
v.requestLayout();
final int initialHeight = v.getMeasuredHeight();
if(quick){
v.setVisibility(View.GONE);
v.requestLayout();
}else{
android.view.animation.Animation a = new android.view.animation.Animation()
{
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if(interpolatedTime == 1){
v.setVisibility(View.GONE);
}else{
v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
v.requestLayout();
}
}
#Override
public boolean willChangeBounds() {
return true;
}
};
// 1dp/ms
int duration = (int)( initialHeight/ v.getContext().getResources().getDisplayMetrics().density);
if(duration> 500)duration=500;
a.setDuration(duration);
v.startAnimation(a);
}
}
If if create a form and need a section, i create a instance of this class and add the controls.
You might need to turn the hardware acceleration on in order to get the best performance
edit:
Usage is like:
section s = new section(context, section_name, section_state);
s.container.addView([your view 1]);
s.container.addView([your view 2]);
s.container.addView([your view 3]);
//...
form.addView(s);
I'm making an app that just displays a clock, but I want is so that everytime a user touches the screen it changes the color of the text (from a list of preselected colors in a colors.xml file) but I haven't got a clue where to start. Can someone point me in the right direction?
Here's the main activity:
public class MainActivity extends Activity {
private static final Random RANDOM = new Random();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_main);
Handler handler = new RandomMoveHandler((TextView) findViewById(R.id.digitalClock1));
handler.sendEmptyMessage(0);
}
// Make the handler subclass static because of this: http://stackoverflow.com/a/11408340/111777
private static class RandomMoveHandler extends Handler {
private final WeakReference<TextView> textViewWeakReference;
private RandomMoveHandler(TextView textView) {
this.textViewWeakReference = new WeakReference<TextView>(textView);
}
#Override
public void handleMessage(Message msg) {
TextView textView = textViewWeakReference.get();
if (textView == null) {
Log.i(TAG, "WeakReference is gone so giving up.");
return;
}
int x = RANDOM.nextInt(350 - 100);
int y = RANDOM.nextInt(800 - 100);
Log.i(TAG, String.format("Moving text view to (%d, %d)", x, y));
textView.setX(x);
textView.setY(y);
//change the text position here
this.sendEmptyMessageDelayed(0, 30000);
}
}
private static final String TAG = MainActivity.class.getSimpleName();
}
and here's the layout xml:
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context=".MainActivity"
android:background="#color/black" >
<DigitalClock
android:id="#+id/digitalClock1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DigitalClock"
android:textColor="#color/ics_blue"
android:textSize="28sp" />
I haven't making deal with DigitalClock but I think, at first, you should reference DigitalClock variable, not TextView. And second, to intercept touch event you need to override onTouckEvent method of your activity, it will callback everytime user touches the screen.
You should follow these steps
Use a TimerTask to.continusly show the time
Implement a touchlistener on that clock view
like this
view.setOnTouchListener
Make an array Colors like this
int[] colr={Color.BLACK,Color.BLUE};
and use random index in your touch event andset it as your color of the view