I am creating a live wallpaper settings activity. It has a few seek bars and an Ad Mob ad view. Every time I touch one of the seek bars the ad temporarily disappears and then reloads a new add. However, the ad does not change when any of the other preferences are touched. How do I stop the ad from reloading when a seek bar preference is touched? I am pretty stumped on this one :-). Code follows:
The ad preference class:
import android.app.Activity;
import android.content.Context;
import android.preference.Preference;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.google.ads.AdRequest;
import com.google.ads.AdSize;
import com.google.ads.AdView;
public class AdPreference extends Preference {
public AdPreference(Context context, AttributeSet attrs, int defStyle) {super (context, attrs, defStyle);}
public AdPreference(Context context, AttributeSet attrs) {super(context, attrs);}
public AdPreference(Context context) {super(context);}
#Override
protected View onCreateView(ViewGroup parent) {
// this will create the linear layout defined in ads_layout.xml
View view = super.onCreateView(parent);
// the context is a PreferenceActivity
Activity activity = (Activity)getContext();
// Create the adView
AdView adView = new AdView(activity, AdSize.BANNER, "a151390e12917b5");
((LinearLayout)view).addView(adView);
// Initiate a generic request to load it with an ad
AdRequest request = new AdRequest();
request.addTestDevice("23392C83B8B55DE893A18286CB92DDA2");
request.addTestDevice("E1BAA0317138AEE05268B2E4F76B2D3F");
adView.loadAd(request);
return view;
}
}
The seek bar class:
import android.content.Context;
import android.content.res.TypedArray;
import android.preference.Preference;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
public class SeekBarPreference extends Preference implements OnSeekBarChangeListener {
private final String TAG = getClass().getName();
private static final String ANDROIDNS="http://schemas.android.com/apk/res/android";
private static final String ROBOBUNNYNS="http://robobunny.com";
private static final int DEFAULT_VALUE = 50;
private int mMaxValue = 100;
private int mMinValue = 0;
private int mInterval = 1;
private int mCurrentValue;
private String mUnitsLeft = "";
private String mUnitsRight = "";
private SeekBar mSeekBar;
private TextView mStatusText;
public SeekBarPreference(Context context, AttributeSet attrs) {
super(context, attrs);
initPreference(context, attrs);
}
public SeekBarPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initPreference(context, attrs);
}
private void initPreference(Context context, AttributeSet attrs) {
setValuesFromXml(attrs);
mSeekBar = new SeekBar(context, attrs);
mSeekBar.setMax(mMaxValue - mMinValue);
mSeekBar.setOnSeekBarChangeListener(this);
}
private void setValuesFromXml(AttributeSet attrs) {
mMaxValue = attrs.getAttributeIntValue(ANDROIDNS, "max", 100);
mMinValue = attrs.getAttributeIntValue(ROBOBUNNYNS, "min", 0);
mUnitsLeft = getAttributeStringValue(attrs, ROBOBUNNYNS, "unitsLeft", "");
String units = getAttributeStringValue(attrs, ROBOBUNNYNS, "units", "");
mUnitsRight = getAttributeStringValue(attrs, ROBOBUNNYNS, "unitsRight", units);
try {
String newInterval = attrs.getAttributeValue(ROBOBUNNYNS, "interval");
if(newInterval != null)
mInterval = Integer.parseInt(newInterval);
}
catch(Exception e) {
Log.e(TAG, "Invalid interval value", e);
}
}
private String getAttributeStringValue(AttributeSet attrs, String namespace, String name, String defaultValue) {
String value = attrs.getAttributeValue(namespace, name);
if(value == null)
value = defaultValue;
return value;
}
#Override
protected View onCreateView(ViewGroup parent){
RelativeLayout layout = null;
try {
LayoutInflater mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layout = (RelativeLayout)mInflater.inflate(R.layout.seek_bar_preference, parent, false);
}
catch(Exception e)
{
Log.e(TAG, "Error creating seek bar preference", e);
}
return layout;
}
#Override
public void onBindView(View view) {
super.onBindView(view);
try
{
// move our seekbar to the new view we've been given
ViewParent oldContainer = mSeekBar.getParent();
ViewGroup newContainer = (ViewGroup) view.findViewById(R.id.seekBarPrefBarContainer);
if (oldContainer != newContainer) {
// remove the seekbar from the old view
if (oldContainer != null) {
((ViewGroup) oldContainer).removeView(mSeekBar);
}
// remove the existing seekbar (there may not be one) and add ours
newContainer.removeAllViews();
newContainer.addView(mSeekBar, ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
}
catch(Exception ex) {
Log.e(TAG, "Error binding view: " + ex.toString());
}
updateView(view);
}
/**
* Update a SeekBarPreference view with our current state
* #param view
*/
protected void updateView(View view) {
try {
RelativeLayout layout = (RelativeLayout)view;
mStatusText = (TextView)layout.findViewById(R.id.seekBarPrefValue);
mStatusText.setText(String.valueOf(mCurrentValue));
mStatusText.setMinimumWidth(30);
mSeekBar.setProgress(mCurrentValue - mMinValue);
TextView unitsRight = (TextView)layout.findViewById(R.id.seekBarPrefUnitsRight);
unitsRight.setText(mUnitsRight);
TextView unitsLeft = (TextView)layout.findViewById(R.id.seekBarPrefUnitsLeft);
unitsLeft.setText(mUnitsLeft);
}
catch(Exception e) {
Log.e(TAG, "Error updating seek bar preference", e);
}
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
int newValue = progress + mMinValue;
if(newValue > mMaxValue)
newValue = mMaxValue;
else if(newValue < mMinValue)
newValue = mMinValue;
else if(mInterval != 1 && newValue % mInterval != 0)
newValue = Math.round(((float)newValue)/mInterval)*mInterval;
// change rejected, revert to the previous value
if(!callChangeListener(newValue)){
seekBar.setProgress(mCurrentValue - mMinValue);
return;
}
// change accepted, store it
mCurrentValue = newValue;
mStatusText.setText(String.valueOf(newValue));
persistInt(newValue);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
notifyChanged();
}
#Override
protected Object onGetDefaultValue(TypedArray ta, int index){
int defaultValue = ta.getInt(index, DEFAULT_VALUE);
return defaultValue;
}
#Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
if(restoreValue) {
mCurrentValue = getPersistedInt(mCurrentValue);
}
else {
int temp = 0;
try {
temp = (Integer)defaultValue;
}
catch(Exception ex) {
Log.e(TAG, "Invalid default value: " + defaultValue.toString());
}
persistInt(temp);
mCurrentValue = temp;
}
}
}
The xml layout for the settings activity:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:robobunny="http://robobunny.com"
android:key="disco_wormhole_settings" >
<com.package.name.and.AdPreference
android:layout_width="fill_parent"
androidLlayout_height="wrap_content" />
<Preference
android:key="title"
android:summary="#string/settings_summary"
android:title="#string/settings_title" />
<com.package.name.and.SeekBarPreference
android:defaultValue="50"
android:key="flight_speed"
android:max="100"
android:progressDrawable="#drawable/seek_bar_progress"
android:summary="#string/flight_speed_summary"
android:title="#string/flight_speed_title"
robobunny:min="1"
robobunny:unitsLeft=""
robobunny:unitsRight="%" />
<com.package.name.and.SeekBarPreference
android:defaultValue="30"
android:key="num_rings"
android:max="40"
android:progressDrawable="#drawable/seek_bar_progress"
android:summary="#string/num_rings_summary"
android:title="#string/num_rings_title"
robobunny:min="1"
robobunny:unitsLeft=""
robobunny:unitsRight="" />
<com.package.name.and.SeekBarPreference
android:defaultValue="50"
android:key="particle_speed"
android:max="100"
android:progressDrawable="#drawable/seek_bar_progress"
android:summary="#string/particle_speed_summary"
android:title="#string/particle_speed_title"
robobunny:min="1"
robobunny:unitsLeft=""
robobunny:unitsRight="%" />
<Preference
android:summary="#string/colors_summary"
android:title="#string/colors_title" >
</Preference>
<Preference
android:defaultValue="0xff7d9fff"
android:key="color_one"
android:title="#string/color_one" >
</Preference>
<Preference
android:defaultValue="0xffff4b31"
android:key="color_two"
android:title="#string/color_two" >
</Preference>
<Preference
android:defaultValue="0xff64ff46"
android:key="color_three"
android:title="#string/color_three" >
</Preference>
<CheckBoxPreference
android:defaultValue="true"
android:key="use_space_dust"
android:title="#string/use_space_dust_title" />
<Preference
android:key="spacer"
android:title="#string/single_space" />
<Preference>
</Preference>
</PreferenceScreen>
Thanks in advance for your help,
Chris
I think onCreateView will be called multiple times so an ad request will be created every time. Try moving your admob code to something like onBind or onAttached.
Related
I'm trying to create a numberpicker in Android but the wheel only increase by 1. I want to increase by 0.1. I've looked up on the net but I've found a formated array of floats disabling the wheel. Please help and sorry for the grammar, I'm learning.
You can do it with custom strings:
NumberPicker picker = new NumberPicker(this);
picker.setMinValue(0);
picker.setMaxValue(100);
picker.setDisplayedValues( new String[] { "0.0", "0.1", ..., "10.0" } );
double = picker.getValue() / 10.0;
You can create your own DecimalPicker view based on ElegantNumberButton.
Add following classes to your project:
/path/to/your/DecimalPicker.java
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;
import ru.alanov.cashbox.R;
import ru.alanov.cashbox.Utils;
public class DecimalPicker extends RelativeLayout {
private Context context;
private AttributeSet attrs;
private int styleAttr;
private OnClickListener mListener;
private double initialNumber, finalNumber, lastNumber, currentNumber;
private EditText editText;
private String format;
private OnValueChangeListener onValueChangeListener;
public DecimalPicker(Context context) {
super(context);
this.context = context;
initView();
}
public DecimalPicker(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
this.attrs = attrs;
initView();
}
public DecimalPicker(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
this.attrs = attrs;
this.styleAttr = defStyleAttr;
initView();
}
private void initView(){
inflate(context, R.layout.decimal_picker, this);
final Resources res = getResources();
final int defaultColor = res.getColor(R.color.colorPrimary);
final int defaultTextColor = res.getColor(R.color.colorText);
final Drawable defaultDrawable = res.getDrawable(R.drawable.decimal_picker_shape);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DecimalPicker, styleAttr, 0);
initialNumber = a.getInt(R.styleable.DecimalPicker_initialNumber, 0);
finalNumber = a.getInt(R.styleable.DecimalPicker_finalNumber, Integer.MAX_VALUE);
float textSize = a.getDimension(R.styleable.DecimalPicker_textSize, 24);
int color = a.getColor(R.styleable.DecimalPicker_backGroundColor,defaultColor);
int textColor = a.getColor(R.styleable.DecimalPicker_textColor,defaultTextColor);
Drawable drawable = a.getDrawable(R.styleable.DecimalPicker_backgroundDrawable);
Button buttonMinus = (Button) findViewById(R.id.subtract_btn);
Button buttonPlus = (Button) findViewById(R.id.add_btn);
editText = (EditText) findViewById(R.id.number_counter);
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_NEXT) {
String num = ((EditText) v).getText().toString();
setNumber(num, true);
}
return false;
}
});
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
String value = s.toString().trim();
double valueDouble = -1;
try {
valueDouble = parseDouble(value.isEmpty() ? "0" : value);
} catch (NumberFormatException e) {
e.printStackTrace();
}
if (valueDouble >= 0){
lastNumber = currentNumber;
currentNumber = valueDouble;
callListener(DecimalPicker.this);
}
}
});
LinearLayout mLayout = (LinearLayout) findViewById(R.id.decimal_picker_layout);
buttonMinus.setTextColor(textColor);
buttonPlus.setTextColor(textColor);
editText.setTextColor(textColor);
buttonMinus.setTextSize(textSize);
buttonPlus.setTextSize(textSize);
editText.setTextSize(textSize);
if (drawable == null){
drawable = defaultDrawable;
}
assert drawable != null;
drawable.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC));
if (Build.VERSION.SDK_INT > 16)
mLayout.setBackground(drawable);
else
mLayout.setBackgroundDrawable(drawable);
editText.setText(String.valueOf(initialNumber));
currentNumber = initialNumber;
lastNumber = initialNumber;
buttonMinus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View mView) {
double num = parseDouble(editText.getText().toString());
setNumber(String.valueOf(num - 1)/*, true*/);
}
});
buttonPlus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View mView) {
double num = parseDouble(editText.getText().toString());
setNumber(String.valueOf(num + 1)/*, true*/);
}
});
a.recycle();
}
private void callListener(View view){
if (mListener != null)
mListener.onClick(view);
if (onValueChangeListener != null && lastNumber != currentNumber)
onValueChangeListener.onValueChange(this, lastNumber, currentNumber);
}
public String getNumber(){
return String.valueOf(currentNumber);
}
public void setNumber(String number) {
try {
double n = parseDouble(number);
if (n > finalNumber)
n = finalNumber;
if (n < initialNumber)
n = initialNumber;
if (format != null) {
String num = String.format(Utils.getCurrentLocale(getContext()), format, n);
num = removeTrailingZeroes(num);
editText.setText(num);
} else
editText.setText(String.valueOf(number));
lastNumber = currentNumber;
currentNumber = parseDouble(editText.getText().toString());
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
private double parseDouble(String str) throws NumberFormatException {
return Double.parseDouble(str.replace(",","."));
}
private String removeTrailingZeroes(String num) {
NumberFormat nf = NumberFormat.getInstance();
if (nf instanceof DecimalFormat) {
DecimalFormatSymbols sym = ((DecimalFormat) nf).getDecimalFormatSymbols();
char decSeparator = sym.getDecimalSeparator();
String[] split = num.split((decSeparator == '.' ? "\\" : "") + String.valueOf(decSeparator));
if (split.length == 2 && split[1].replace("0", "").isEmpty())
num = split[0];
}
return num;
}
public void setNumber(String number, boolean notifyListener){
setNumber(number);
if (notifyListener)
callListener(this);
}
public void setOnClickListener(OnClickListener onClickListener) {
mListener = onClickListener;
}
public void setOnValueChangeListener(OnValueChangeListener onValueChangeListener){
this.onValueChangeListener = onValueChangeListener;
}
public interface OnClickListener {
void onClick(View view);
}
public interface OnValueChangeListener {
void onValueChange(DecimalPicker view, double oldValue, double newValue);
}
public void setRange(Double startingNumber, Double endingNumber) {
initialNumber = startingNumber;
finalNumber = endingNumber;
}
public void setFormat(String format){
this.format = format;
}
}
/layout/decimal_picker.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:id="#+id/decimal_picker_layout"
android:layout_height="wrap_content">
<Button
android:id="#+id/subtract_btn"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:text="-"
android:background="#android:color/transparent" />
<EditText
android:id="#+id/number_counter"
android:gravity="center"
android:imeOptions="flagNoExtractUi"
android:inputType="numberDecimal"
android:text = "1"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#android:color/transparent" />
<Button
android:id="#+id/add_btn"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:text="+"
android:background="#android:color/transparent" />
</LinearLayout>
/drawable/decimal_picker_shape.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="4dp" />
</shape>
/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DecimalPicker">
<attr name="backGroundColor" format="color"/>
<attr name="initialNumber" format="integer"/>
<attr name="finalNumber" format="integer" />
<attr name="textColor" format="color"/>
<attr name="textSize" format="dimension"/>
<attr name="backgroundDrawable" format="reference"/>
<attr name="decimalFormat" format="reference"/>
</declare-styleable>
</resources>
In /values/colors.xml add <color name="colorText">#FFFFFF</color>
Usage example:
In your activity layout place
<path.to.your.DecimalPicker
android:id="#+id/decimal_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"/>
In your Activity#onCreate() place
DecimalPicker decimalPicker = (DecimalPicker) view.findViewById(R.id.decimal_picker);
decimalPicker.setFormat("%.3f");//Weight format
decimalPicker.setOnValueChangeListener(new DecimalPicker.OnValueChangeListener() {
#Override
public void onValueChange(DecimalPicker picker, double oldValue, double newValue) {
//Do what you want to handle value change.
}
});
The result of efforts:
Click on +/- will change integer part of number. If you want to change fractional part - click on number and edit it by hands. If there is no fractional part you will see a integer part only without zeroes.
Alternatively you can use this handy NumberPicker Kotlin extension dialog which scales your Double values into a fitting Int range and converts the Int values back to Doubles before calling any of the callback. It basicallly hides away the fact that NumberPicker only supports Int and adds support for Double!
Here's the Fragment extension you need to copy & paste:
fun Fragment.showNumberPickerDialog(
title: String,
value: Double,
range: ClosedRange<Double>,
stepSize: Double,
formatToString: (Double) -> String,
valueChooseAction: (Double) -> Unit
) {
val numberPicker = NumberPicker(context).apply {
setFormatter { formatToString(it.toDouble() * stepSize) }
wrapSelectorWheel = false
minValue = (range.start / stepSize).toInt()
maxValue = (range.endInclusive / stepSize).toInt()
this.value = (value.toDouble() / stepSize).toInt()
// NOTE: workaround for a bug that rendered the selected value wrong until user scrolled, see also: https://stackoverflow.com/q/27343772/3451975
(NumberPicker::class.java.getDeclaredField("mInputText").apply { isAccessible = true }.get(this) as EditText).filters = emptyArray()
}
MaterialAlertDialogBuilder(context)
.setTitle(title)
.setView(numberPicker)
.setPositiveButton("OK") { _, _ -> valueChooseAction(numberPicker.value.toDouble() * stepSize) }
.setNeutralButton("Cancel") { _, _ -> /* do nothing, closes dialog automatically */ }
.show()
}
Then use it like this:
showNumberPickerDialog(
title = "Your Weight",
value = 75.0, // in kilograms
range = 10.0 .. 300.0,
formatToString = { "$it kg" },
valueChooseAction = { saveNewWeight(it) }
)
I'm new to Android and I have been struggling to draw on canvas a rectangle and text where the size of the rectangle, the text and the color depend on values selected from a MySql database.
I managed to get data selected inside my Activity, but I just can't figure it out how to pass the MySql data to the ondraw() method so I can draw the rectangle and text using the data.
Any help will be greatly appreciated.
public class MyTankActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
int useridInt = intent.getIntExtra("userid", -1);
String userid = useridInt+"";
Response.Listener<String> responseListener = new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jsonResponse = new JSONObject(response);
int success = jsonResponse.getInt("success");
JSONArray tank_data = jsonResponse.getJSONArray("tank_data");
if (success == 1) {
int i;
for(i=0;i<tank_data.length();i++){
// Log.v("Result--", "" + tank_data.getString(i));
JSONObject tankObj = tank_data.getJSONObject(0);
String location = (String) tankObj.getString("Location");
String color = (String) tankObj.getString("Color");
String Level = (String) tankObj.getString("Level");
}
} else {
// No records found in database
}
} catch (JSONException e) {
e.printStackTrace();
}
}
};
MyTankRequest myTankRequest = new MyTankRequest(userid, responseListener);
RequestQueue queue = Volley.newRequestQueue(MyTankActivity.this);
queue.add(myTankRequest);
setContentView(new TankView(this));
}
}
XML layout:
<?xml version="1.0" encoding="utf-8"?>
<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.nivelsonic.nivelsonic.MyTankActivity"
android:background="#AEECFF">
<com.nivelsonic.nivelsonic.TankView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
TankView class:
package com.nivelsonic.nivelsonic;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class TankView extends View {
private Paint _paintTank = new Paint();
private Path _path = new Path();
public TankView(Context context) {
super(context);
// TODO Auto-generated constructor stub
init(null, 0);
}
public TankView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
init(attrs, 0);
}
public TankView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
init(attrs, 0);
}
private void init(AttributeSet attrs, int defStyle) {
_paintTank.setColor(Color.RED);
_paintTank.setAntiAlias(true);
_paintTank.setStyle(Paint.Style.STROKE);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
// canvas.drawText();
// canvas.rect();
}
}
One suggestion:
Move JSON data to global variables
public class MyTankActivity extends AppCompatActivity {
String location;
String color;
String Level;
//....
And change setContentView with different constructor of TankView that allows to pass the obtained data:
setContentView(new TankView(this, location, color, Level));
Modify the constructor of TankView accordingly, also add some variables to TankView to store this values:
public TankView(Context context, String loc, String color, String level) {
super(context);
init(null, 0);
this.location = loc;// ... do the same with color/level
...
}
Modify the onDraw after you get the data
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
//using this.Location, color, text
// canvas.drawText();.
// canvas.rect();
}
Recently I've been trying to create a CustomView.
I am following the tutorial and did as directed but when i tried to run the code my CustomView was not displayed on my android screen.
My code for attrs.xml is:-
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TimeView">
<attr name="text" format="string"/>
<attr name="setColor" format="boolean"/>
</declare-styleable>
</resources>
Here is the code for my CustomView i.e TimeView.java:-
package com.example.custom;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.widget.TextView;
public class TimeView
extends TextView{
public String titleText;
public boolean color;
public TimeView(Context context) {
super(context);
setTimeView();
// TODO Auto-generated constructor stub
}
public TimeView(Context c,AttributeSet as)
{
super(c,as);
TypedArray ty=c.obtainStyledAttributes(as,R.styleable.TimeView);
int count=ty.getIndexCount();
try{
for(int i=0;i<count;i++)
{
int attr=ty.getIndex(i);
if(attr==R.styleable.TimeView_text)
{
titleText=ty.getString(attr);
}
else if(attr==R.styleable.TimeView_setColor)
{
color=ty.getBoolean(attr, false);
decorate();
}
}
}
finally
{
ty.recycle();
}
}
public TimeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setTimeView();
}
public void setTimeView()
{
SimpleDateFormat sdf=new SimpleDateFormat("hh.mm aa");
String time=sdf.format(Calendar.getInstance().getTime());
if(this.titleText!=null)
{
setText(this.titleText+" "+time);
}
else
setText(time);
}
public void decorate()
{
if(this.color==true)
{
setShadowLayer(4, 2, 2, Color.rgb(250, 00, 250));
setBackgroundColor(Color.CYAN);
}
else{
setBackgroundColor(Color.RED);
}
}
}
and lastly here is the code for my activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res/com.example.custom"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.custom.MainActivity" >
<com.example.custom.TimeView
android:id="#+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="40sp"
custom:text="My View"
custom:setColor="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world" />
</LinearLayout>
I am getting this result:-
I don't know where i am doing mistake.
Please help me!
Thank you in advance.
Just overwrite your code with my code it's working. You just make mistake while retrieving attributes.
Don't forget to add your package name at first line
import java.text.SimpleDateFormat;
import java.util.Calendar;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.widget.TextView;
public class TimeView
extends TextView {
public String titleText;
public boolean color;
public TimeView(Context context) {
super(context);
setTimeView(context, null);
}
public TimeView(Context c, AttributeSet as) {
super(c, as);
setTimeView(c, as);
}
public TimeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setTimeView(context, attrs);
}
public void setTimeView(Context c, AttributeSet attrs) {
TypedArray a;
if (attrs != null) {
a = c.getTheme().obtainStyledAttributes(
attrs,
R.styleable.BLProgress,
0, 0);
} else {
throw new IllegalArgumentException("Must have to pass the attributes");
}
try {
titleText = a.getString(R.styleable.TimeView_text);
color = a.getBoolean(R.styleable.TimeView_setColor, false);
} finally {
a.recycle();
}
SimpleDateFormat sdf = new SimpleDateFormat("hh.mm aa");
String time = sdf.format(Calendar.getInstance().getTime());
if (this.titleText != null) {
setText(this.titleText + " " + time);
} else
setText(time);
decorate();
}
public void decorate() {
if (color) {
setShadowLayer(4, 2, 2, Color.rgb(250, 00, 250));
setBackgroundColor(Color.CYAN);
} else {
setBackgroundColor(Color.RED);
}
}
}
here is the screen shot ..
I'm using a custom dialog preference to generate a seekbar in my preferences menu.
After researching seekbar implementation, to obtain the float value I need from the seekbar I have written the following code:
public void onProgressChanged(SeekBar seek, int newValue,
boolean fromTouch) {
// Round the value to the closest integer value.
if (stepSize >= 1) {
value = Math.round(newValue/stepSize)*stepSize;
}
else {
value = newValue;
}
// Set the valueText text.
float sValue = newValue*10;
sValue = sValue/100;
valueText.setText(Float.toString(sValue));
Which produces the float that I want. However, I want to be able to use this float in my main activity. I have attempted to store it using SharedPreferences using:
userPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
Editor editor = userPrefs.edit()
editor.putFloat("mpm", sValue);
editor.commit();
Which is how I've learned to use SharedPreferences in a class extending Activity.
However as this seekbar extends Dialogue Preference I cannot use
getBaseContext()
As I get the error that the method getBaseContext is undefined for this type.
I have tried changing getBaseContext() to getContext() but this has been unsuccessful although that may be because I am unfamiliar with this implementation.
How can I save this float from the dialogue preference and use the value in a different class?
The code I am using to retrieve SharedPreferences:
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.logbook);
initialise();
userPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
list = userPrefs.getString("list", "10");
userswallstring = userPrefs.getString("height", "10.0");
try {
usersWall = Float.valueOf(userswallstring.trim());
} catch (Exception e) {
// TODO: handle exception
}
mpm = userPrefs.getFloat("mpm", 2);
Mpm.class:
package com.gbclimber.ep;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.TypedArray;
import android.preference.DialogPreference;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;
public class Mpm extends
DialogPreference implements SeekBar.OnSeekBarChangeListener {
// Layout widgets.
private SeekBar seekBar = null;
private TextView valueText = null;
// Custom xml attributes.
private int maximumValue = 0;
private int minimumValue = 0;
private int stepSize = 0;
private String units = null;
private int value = 0;
SharedPreferences userPrefs;
/**
* The SeekBarDialogPreference constructor.
* #param context of this preference.
* #param attrs custom xml attributes.
*/
public Mpm(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.Mpm);
maximumValue = typedArray.getInteger(
R.styleable.Mpm_maximumValue, 0);
minimumValue = typedArray.getInteger(
R.styleable.Mpm_minimumValue, 0);
stepSize = typedArray.getInteger(
R.styleable.Mpm_stepSize, 1);
units = typedArray.getString(
R.styleable.Mpm_units);
typedArray.recycle();
}
/**
* {#inheritDoc}
*/
protected View onCreateDialogView() {
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
View view = layoutInflater.inflate(
R.layout.mpmdp, null);
seekBar = (SeekBar)view.findViewById(R.id.seekbar);
valueText = (TextView)view.findViewById(R.id.valueText);
// Get the persistent value and correct it for the minimum value.
value = getPersistedInt(minimumValue) - minimumValue;
// You're never know...
if (value < 0) {
value = 0;
}
seekBar.setOnSeekBarChangeListener(this);
seekBar.setKeyProgressIncrement(stepSize);
seekBar.setMax(maximumValue - minimumValue);
seekBar.setProgress(value);
return view;
}
/**
* {#inheritDoc}
*/
public void onProgressChanged(SeekBar seek, int newValue,
boolean fromTouch) {
// Round the value to the closest integer value.
if (stepSize >= 1) {
value = Math.round(newValue/stepSize)*stepSize;
}
else {
value = newValue;
}
// Set the valueText text.
float sValue = newValue*10;
sValue = sValue/100;
userPrefs = PreferenceManager
.getDefaultSharedPreferences(getContext());
Editor editor = userPrefs.edit();
editor.putFloat("mpm", sValue);
editor.commit();
valueText.setText(Float.toString(sValue));
callChangeListener(value);
}
/**
* {#inheritDoc}
*/
public void onStartTrackingTouch(SeekBar seek) {
}
/**
* {#inheritDoc}
*/
public void onStopTrackingTouch(SeekBar seek) {
}
/**
* {#inheritDoc}
*/
public void onClick(DialogInterface dialog, int which) {
// if the positive button is clicked, we persist the value.
if (which == DialogInterface.BUTTON_POSITIVE) {
if (shouldPersist()) {
persistInt(value + minimumValue);
}
}
super.onClick(dialog, which);
}
#Override
protected void onDialogClosed(boolean positiveResult) {
// TODO Auto-generated method stub
super.onDialogClosed(positiveResult);
persistInt(value + minimumValue);
}
}
Just did a quick search but for your context try
this.getDialog().getContext()
I want my preference menu to have something to change the duration of a vibration.
There is no slider tag for prefs.xml, so what is the best way to do this?
I improved the link provided by Macarse, so that the value is saved only on ok button click, and so that you can use #string/... values in the XML file.
Here is the code:
/* The following code was written by Matthew Wiggins
* and is released under the APACHE 2.0 license
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Improvements :
* - save the value on positive button click, not on seekbar change
* - handle #string/... values in xml file
*/
package fr.atcm.carpooling.views.utils;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
public class SeekBarPreference extends DialogPreference implements SeekBar.OnSeekBarChangeListener, OnClickListener
{
// ------------------------------------------------------------------------------------------
// Private attributes :
private static final String androidns="http://schemas.android.com/apk/res/android";
private SeekBar mSeekBar;
private TextView mSplashText,mValueText;
private Context mContext;
private String mDialogMessage, mSuffix;
private int mDefault, mMax, mValue = 0;
// ------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------
// Constructor :
public SeekBarPreference(Context context, AttributeSet attrs) {
super(context,attrs);
mContext = context;
// Get string value for dialogMessage :
int mDialogMessageId = attrs.getAttributeResourceValue(androidns, "dialogMessage", 0);
if(mDialogMessageId == 0) mDialogMessage = attrs.getAttributeValue(androidns, "dialogMessage");
else mDialogMessage = mContext.getString(mDialogMessageId);
// Get string value for suffix (text attribute in xml file) :
int mSuffixId = attrs.getAttributeResourceValue(androidns, "text", 0);
if(mSuffixId == 0) mSuffix = attrs.getAttributeValue(androidns, "text");
else mSuffix = mContext.getString(mSuffixId);
// Get default and max seekbar values :
mDefault = attrs.getAttributeIntValue(androidns, "defaultValue", 0);
mMax = attrs.getAttributeIntValue(androidns, "max", 100);
}
// ------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------
// DialogPreference methods :
#Override
protected View onCreateDialogView() {
LinearLayout.LayoutParams params;
LinearLayout layout = new LinearLayout(mContext);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(6,6,6,6);
mSplashText = new TextView(mContext);
mSplashText.setPadding(30, 10, 30, 10);
if (mDialogMessage != null)
mSplashText.setText(mDialogMessage);
layout.addView(mSplashText);
mValueText = new TextView(mContext);
mValueText.setGravity(Gravity.CENTER_HORIZONTAL);
mValueText.setTextSize(32);
params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
layout.addView(mValueText, params);
mSeekBar = new SeekBar(mContext);
mSeekBar.setOnSeekBarChangeListener(this);
layout.addView(mSeekBar, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
if (shouldPersist())
mValue = getPersistedInt(mDefault);
mSeekBar.setMax(mMax);
mSeekBar.setProgress(mValue);
return layout;
}
#Override
protected void onBindDialogView(View v) {
super.onBindDialogView(v);
mSeekBar.setMax(mMax);
mSeekBar.setProgress(mValue);
}
#Override
protected void onSetInitialValue(boolean restore, Object defaultValue)
{
super.onSetInitialValue(restore, defaultValue);
if (restore)
mValue = shouldPersist() ? getPersistedInt(mDefault) : 0;
else
mValue = (Integer)defaultValue;
}
// ------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------
// OnSeekBarChangeListener methods :
#Override
public void onProgressChanged(SeekBar seek, int value, boolean fromTouch)
{
String t = String.valueOf(value);
mValueText.setText(mSuffix == null ? t : t.concat(" " + mSuffix));
}
#Override
public void onStartTrackingTouch(SeekBar seek) {}
#Override
public void onStopTrackingTouch(SeekBar seek) {}
public void setMax(int max) { mMax = max; }
public int getMax() { return mMax; }
public void setProgress(int progress) {
mValue = progress;
if (mSeekBar != null)
mSeekBar.setProgress(progress);
}
public int getProgress() { return mValue; }
// ------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------
// Set the positive button listener and onClick action :
#Override
public void showDialog(Bundle state) {
super.showDialog(state);
Button positiveButton = ((AlertDialog) getDialog()).getButton(AlertDialog.BUTTON_POSITIVE);
positiveButton.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if (shouldPersist()) {
mValue = mSeekBar.getProgress();
persistInt(mSeekBar.getProgress());
callChangeListener(Integer.valueOf(mSeekBar.getProgress()));
}
((AlertDialog) getDialog()).dismiss();
}
// ------------------------------------------------------------------------------------------
}
EDIT :
Here is a screenshot :
EDIT : on arlomedia's demand, here are all the needed pieces of code (I just recreated a new projet, it is perfectly working. I corrected some bugs in SeekBarPreference class, so don't forget to re-copy/paste it) :
MainActivity :
package fr.at.testsliderpref;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch(item.getItemId()) {
case R.id.menu_settings : {
startActivity(new Intent(this, SettingsActivity.class));
break;
}
}
return true;
}
}
SettingsActivity :
package fr.at.testsliderpref;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import fr.at.testsliderpref.utils.SeekBarPreference;
public class SettingsActivity extends PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
// Call super :
super.onCreate(savedInstanceState);
// Set the activity's fragment :
getFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit();
}
public static class SettingsFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener {
private SeekBarPreference _seekBarPref;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.activity_settings);
// Get widgets :
_seekBarPref = (SeekBarPreference) this.findPreference("SEEKBAR_VALUE");
// Set listener :
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
// Set seekbar summary :
int radius = PreferenceManager.getDefaultSharedPreferences(this.getActivity()).getInt("SEEKBAR_VALUE", 50);
_seekBarPref.setSummary(this.getString(R.string.settings_summary).replace("$1", ""+radius));
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// Set seekbar summary :
int radius = PreferenceManager.getDefaultSharedPreferences(this.getActivity()).getInt("SEEKBAR_VALUE", 50);
_seekBarPref.setSummary(this.getString(R.string.settings_summary).replace("$1", ""+radius));
}
}
}
layout > activity_main.xml :
<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=".MainActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/textview_text" />
</RelativeLayout>
menu > main.xml :
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/menu_settings"
android:title="#string/menu_settings"
android:icon="#android:drawable/ic_menu_preferences"/>
</menu>
xml > activity_settings.xml :
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<fr.at.testsliderpref.utils.SeekBarPreference
android:defaultValue="50"
android:dialogMessage="#string/settings_dialog_message"
android:key="SEEKBAR_VALUE"
android:max="100"
android:summary="#string/settings_summary"
android:text="#string/settings_unit"
android:title="#string/settings_title" />
</PreferenceScreen>
values > strings.xml :
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">TestSliderPref</string>
<string name="textview_text">SeekBarPreference test</string>
<string name="menu_settings">Settings</string>
<string name="settings_dialog_message">Here comes a message</string>
<string name="settings_summary">Current value is $1</string>
<string name="settings_unit">Km</string>
<string name="settings_title">Here comes the title</string>
</resources>
Don't forget to add your SettingsActivity to the manifest, and it should be OK.
You could create your own Preference class that extends DialogPreference and shows a SeekBar as the dialog view.
An easy way to achieve this is to add an empty preference to your preferences.xml which uses a layout including a seekbar.
In your preferences.xml add
<Preference android:layout="#layout/sliderlayout" />
under layouts add the sliderlayout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<SeekBar
android:id="#+id/seekBar1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
If you already moved to androidX you can simply use androidx.preference.SeekBarPreference. Documentation here and here
Define it in XML like this:
<SeekBarPreference
app:defaultValue="8"
app:key="COUNT_SPEED"
app:title="Fast count speed"
/>
Note: Now in Android Studio (my current version is 3.2.1) auto-suggestions doesn't work for androidx.preference.PreferenceScreen in prefs.xml, but don't worry inflating settings from xml with setPreferencesFromResource() works fine. All needed attributes names and component references you can find here.
This is good Slider Preference component - Android Slider Preference Library
https://github.com/jayschwa/AndroidSliderPreference
One more implementation of this.
All Credit goes to Tim Autin.
I wanted to have the values displayed loaded from XML Arrays
It looks like this
Code - this now extends ListPreference
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.preference.ListPreference;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
public class SeekBarListPreference extends ListPreference implements SeekBar.OnSeekBarChangeListener, View.OnClickListener {
// ------------------------------------------------------------------------------------------
// Private attributes :
private static final String androidns = "http://schemas.android.com/apk/res/android";
private SeekBar mSeekBar;
private TextView mSplashText, mValueText;
private Context mContext;
private String mDialogMessage;
// ------------------------------------------------------------------------------------------
public SeekBarListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
// Get string value for dialogMessage :
int mDialogMessageId = attrs.getAttributeResourceValue(androidns, "dialogMessage", 0);
if (mDialogMessageId == 0)
mDialogMessage = attrs.getAttributeValue(androidns, "dialogMessage");
else mDialogMessage = mContext.getString(mDialogMessageId);
}
// ------------------------------------------------------------------------------------------
// DialogPreference methods :
#Override
protected View onCreateDialogView() {
LinearLayout.LayoutParams params;
LinearLayout layout = new LinearLayout(mContext);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(6, 6, 6, 6);
mSplashText = new TextView(mContext);
mSplashText.setPadding(30, 10, 30, 10);
if (mDialogMessage != null)
mSplashText.setText(mDialogMessage);
layout.addView(mSplashText);
mValueText = new TextView(mContext);
mValueText.setGravity(Gravity.CENTER_HORIZONTAL);
mValueText.setTextSize(32);
params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
layout.addView(mValueText, params);
mSeekBar = new SeekBar(mContext);
mSeekBar.setOnSeekBarChangeListener(this);
layout.addView(mSeekBar, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
setProgressBarValue();
return layout;
}
#Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
// do not call super
}
private void setProgressBarValue() {
String mValue = null;
if (shouldPersist()) {
mValue = getValue();
}
final int max = this.getEntries().length - 1;
mSeekBar.setMax(max);
mSeekBar.setProgress(this.findIndexOfValue(mValue));
}
#Override
protected void onBindDialogView(View v) {
super.onBindDialogView(v);
setProgressBarValue();
}
#Override
public void onProgressChanged(SeekBar seek, int value, boolean fromTouch) {
final CharSequence textToDisplay = getEntryFromValue(value);
mValueText.setText(textToDisplay);
}
private CharSequence getEntryFromValue(int value) {
CharSequence[] entries = getEntries();
return value >= 0 && entries != null ? entries[value] : null;
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
#Override
public void showDialog(Bundle state) {
super.showDialog(state);
Button positiveButton = ((AlertDialog) getDialog()).getButton(AlertDialog.BUTTON_POSITIVE);
positiveButton.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if (shouldPersist()) {
final int progressChoice = mSeekBar.getProgress();
setValueIndex(progressChoice);
}
getDialog().dismiss();
}
}
The usage in the preferences file is now
<com.yourfullpackage.SeekBarListPreference
android:defaultValue="0"
android:dialogMessage="#string/time_limit_pref"
android:entries="#array/timeListArray"
android:entryValues="#array/timeListValues"
android:key="time"
android:summary="Select time limit"
android:title="Time"
/>
And the arrays
<string-array name="timeListArray">
<item>10 Seconds</item>
<item>30 Seconds</item>
<item>1 Minute</item>
<item>2 Minutes</item>
<item>Unlimited</item>
</string-array>
<!--This is going to be in seconds-->
<string-array name="timeListValues">
<item>10</item>
<item>30</item>
<item>60</item>
<item>120</item>
<item>0</item>
</string-array>
As a bonus if you already have ListPreferences, you don't need to add anything extra to display the summary as the current value. So this works just fine
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
SetSummaryForPreferenceKey(key);
}
private void SetSummaryForPreferenceKey(String key) {
Preference preference = findPreference(key);
// This works with our new SeekBarPreference
if (preference instanceof ListPreference) {
ListPreference listPref = (ListPreference) preference;
listPref.setSummary(listPref.getEntry());
}
}
I've made a slight improvement on the code provided in the answer by Tim. This simply makes live adjustments to the output value as the user moves the slider, rather than requiring the user to click the "Ok" button for the changes to be made.
This is useful for things like a Music Volume slider, where the user should be able to hear the adjustment in volume as it's being made.
If the user clicks "OK" the new value is maintained. If the user clicks "Cancel" then the original pre-adjustment value is restored.
Thanks and credit should go to Tim, I just added the extra onClick listener and pushed the value updating into the onChange listener.
package fr.atcm.carpooling.views.utils;
/* The following code was written by Matthew Wiggins
* and is released under the APACHE 2.0 license
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Improvements :
* - save the value on positive button click and/or seekbar change
* - restore pre-adjustment value on negative button click
*/
import android.R;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
public class SeekBarPreference extends DialogPreference implements
SeekBar.OnSeekBarChangeListener, OnClickListener {
// ------------------------------------------------------------------------------------------
// Private attributes :
private static final String androidns = "http://schemas.android.com/apk/res/android";
private SeekBar mSeekBar;
private TextView mSplashText, mValueText;
private Context mContext;
private String mDialogMessage, mSuffix;
private int mDefault, mMax, mValue, mOrig = 0;
// ------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------
// Constructor :
public SeekBarPreference(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
// Get string value for dialogMessage :
int mDialogMessageId = attrs.getAttributeResourceValue(androidns,
"dialogMessage", 0);
if (mDialogMessageId == 0)
mDialogMessage = attrs
.getAttributeValue(androidns, "dialogMessage");
else
mDialogMessage = mContext.getString(mDialogMessageId);
// Get string value for suffix (text attribute in xml file) :
int mSuffixId = attrs.getAttributeResourceValue(androidns, "text", 0);
if (mSuffixId == 0)
mSuffix = attrs.getAttributeValue(androidns, "text");
else
mSuffix = mContext.getString(mSuffixId);
// Get default and max seekbar values :
mDefault = attrs.getAttributeIntValue(androidns, "defaultValue", 0);
mMax = attrs.getAttributeIntValue(androidns, "max", 100);
}
// ------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------
// DialogPreference methods :
#Override
protected View onCreateDialogView() {
LinearLayout.LayoutParams params;
LinearLayout layout = new LinearLayout(mContext);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(6, 6, 6, 6);
mSplashText = new TextView(mContext);
mSplashText.setPadding(30, 10, 30, 10);
if (mDialogMessage != null)
mSplashText.setText(mDialogMessage);
layout.addView(mSplashText);
mValueText = new TextView(mContext);
mValueText.setGravity(Gravity.CENTER_HORIZONTAL);
mValueText.setTextSize(32);
params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
layout.addView(mValueText, params);
mSeekBar = new SeekBar(mContext);
mSeekBar.setOnSeekBarChangeListener(this);
layout.addView(mSeekBar, new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
if (shouldPersist())
mValue = getPersistedInt(mDefault);
mSeekBar.setMax(mMax);
mSeekBar.setProgress(mValue);
return layout;
}
#Override
protected void onBindDialogView(View v) {
super.onBindDialogView(v);
mSeekBar.setMax(mMax);
mSeekBar.setProgress(mValue);
}
#Override
protected void onSetInitialValue(boolean restore, Object defaultValue) {
super.onSetInitialValue(restore, defaultValue);
// Set adjustable value
if (restore)
mValue = shouldPersist() ? getPersistedInt(mDefault) : 0;
else
mValue = (Integer) defaultValue;
// Set original pre-adjustment value
mOrig = mValue;
}
// ------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------
// OnSeekBarChangeListener methods :
#Override
public void onProgressChanged(SeekBar seek, int value, boolean fromTouch) {
String t = String.valueOf(value);
mValueText.setText(mSuffix == null ? t : t.concat(" " + mSuffix));
if (shouldPersist()) {
mValue = mSeekBar.getProgress();
persistInt(mSeekBar.getProgress());
callChangeListener(Integer.valueOf(mSeekBar.getProgress()));
}
}
#Override
public void onStartTrackingTouch(SeekBar seek) {
}
#Override
public void onStopTrackingTouch(SeekBar seek) {
}
public void setMax(int max) {
mMax = max;
}
public int getMax() {
return mMax;
}
public void setProgress(int progress) {
mValue = progress;
if (mSeekBar != null)
mSeekBar.setProgress(progress);
}
public int getProgress() {
return mValue;
}
// ------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------
// Set the positive button listener and onClick action :
#Override
public void showDialog(Bundle state) {
super.showDialog(state);
Button positiveButton = ((AlertDialog) getDialog())
.getButton(AlertDialog.BUTTON_POSITIVE);
Button negativeButton = ((AlertDialog) getDialog())
.getButton(AlertDialog.BUTTON_NEGATIVE);
positiveButton.setOnClickListener(cListenPos);
negativeButton.setOnClickListener(cListenNeg);
}
View.OnClickListener cListenPos = new View.OnClickListener() {
public void onClick(View v) {
if (shouldPersist()) {
mValue = mSeekBar.getProgress();
mOrig = mSeekBar.getProgress();
persistInt(mSeekBar.getProgress());
callChangeListener(Integer.valueOf(mSeekBar.getProgress()));
}
((AlertDialog) getDialog()).dismiss();
}
};
View.OnClickListener cListenNeg = new View.OnClickListener() {
public void onClick(View v) {
if (shouldPersist()) {
mValue = mOrig;
persistInt(mOrig);
callChangeListener(Integer.valueOf(mOrig));
}
((AlertDialog) getDialog()).dismiss();
}
};
#Override
public void onClick(View v) {}
// ------------------------------------------------------------------------------------------
}
One small improvement of Tim's answer and its derivates:
protected void onBindDialogView(View v) {
super.onBindDialogView(v);
mSeekBar.setMax(mMax);
mSeekBar.setProgress(mValue);
String t = String.valueOf(mValue);
mValueText.setText(mSuffix == null ? t : t.concat(" " + mSuffix));
}
It will populate current value at start which is otherwise empty until you actually slide the slider.