I used settings activity to let a user select from different resources in my android app. All preference items are checkbox. I've defined a custom layout for each preference and connect it to the checkbox. When I click the preference item it is working properly but I can't understand at which position the preference is. So I want to change the imageview of the custom layout programmatically when I click on the preference item. Is there a way to do that?
Below, the second item is the default text, I want to change the layout to the first one. Everything is working but I want to change the plus image to another image programmatically when I click on this item (to understand that it is checked). Since this is settings activity, I couldn't find a way (there is no findviewbyid etc..) Android behaves the whole custom layout as a checkbox (you can click anywhere on the line)
Settings.xml
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference
android:layout="#layout/source_item"
android:key="gundem_haberturk"
android:title="#string/haberturk"
android:defaultValue="false"/>
</PreferenceScreen>
Custom Layout.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/layout_source_item"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/iv_source_logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxWidth="100dp"
android:maxHeight="40dp"
android:src="#drawable/logo_haberturk"/>
<TextView
android:id="#+id/tv_source_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Habertürk"
android:textSize="22sp"
android:textStyle="bold" />
<ImageView
android:id="#+id/iv_source_selector"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="#drawable/checkbox_unchecked"/>
</RelativeLayout>
Settings Fragment:
public class GundemSettingsFragment extends PreferenceFragmentCompat {
#Override
public void onCreatePreferences(Bundle bundle, String rootKey) {
addPreferencesFromResource(R.xml.gundem_settings);
}
}
Settings Activity:
public class GundemSettingsActivity extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gundem_settings);
Toolbar toolbar = findViewById(R.id.settings_toolbar);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
}
}
Settings Activity xml
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.hhs.haberler.Settings.GundemSettingsActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/settings_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<fragment
android:layout_marginTop="?attr/actionBarSize"
android:id="#+id/settings_fragment"
android:name="com.example.haberler.Settings.GundemSettingsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.design.widget.CoordinatorLayout>
You can create a custom Preference which extends from CheckBoxPreference and use it just like the CheckBoxPreference:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<your.package.name.CustomCheckBoxPreference
android:layout="#layout/custom_checkbox_preference"
android:key="custom_checkbox_pref"
android:title="CustomCheckBoxPreferenceTitle"
android:defaultValue="false"/>
</PreferenceScreen>
Please note that according to the documentation for the android:layout atttribute one has to use specific resource id's for the layout's root ViewGroup as well as the TextViews for title and summary. This will ensure that the customized Preference behaves just like any stock Preference.
By overriding onBindViewHolder(PreferenceViewHolder) you can "find" the ImageView and assign it to a corresponding field ivSourceSelector. And by overriding setChecked() you can swap the drawables when the checked state of the Preference changes.
Having said that, here's the code for CustomCheckBoxPreference:
public class CustomCheckBoxPreference extends CheckBoxPreference {
private ImageView ivSourceSelector;
public CustomCheckBoxPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomCheckBoxPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public CustomCheckBoxPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomCheckBoxPreference(Context context) {
super(context);
}
#Override
public void setChecked(boolean checked) {
super.setChecked(checked);
if(ivSourceSelector != null) {
ivSourceSelector.setImageResource(getResourceId(checked));
}
}
#Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
ivSourceSelector = holder.itemView.findViewById(R.id.iv_source_selector);
if(ivSourceSelector != null){
ivSourceSelector.setImageResource(getResourceId(isChecked()));
}
}
private int getResourceId(boolean checked) {
return checked ? R.drawable.custom_checkbox_checked : R.drawable.custom_checkbox_unchecked;
}
}
Related
I created custom view based on Toolbar:
public class CommonToolbar extends Toolbar {
public CommonToolbar(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.common_toolbar, this, true);
}
common_toolbar.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="32dp"
android:background="#color/colorPrimary"
app:contentInsetStart="0dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/backIcon"
style="#style/ToolbarNavigationButton"
app:srcCompat="#drawable/icon_close" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:layout_toEndOf="#+id/backIcon"
android:gravity="center"
android:text="Title"
android:textStyle="bold" />
</RelativeLayout>
</android.support.v7.widget.Toolbar>
As you can see i set attribute contentInsetStart value to 0, but when i use this Toolbar in any layout file, padding is still present (image). I can modify this by changing contentInsetStart value in destination layout file, but I prefer to keep this in source xml. Why is it happening?
Because your custom view extends Toolbar you are inflating your xml toolbar into a Toolbar. If you take a look at your layout with Layout Inspector, your view hierarchy will look like this:
<CommonToolbar> (Subclass of Toolbar)
<Toolbar>
<RelativeLayout>
...
So when you set contentInsetStart via xml, you are setting it on the inner toolbar. The outer toolbar still has a content inset.
To avoid this you can have Common Toolbar extend a ViewGroup i.e.
public class CommonToolbar extends RelativeLayout {
public CommonToolbar(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.common_toolbar, this, true);
}
Or inflate the common toolbar using an include tag. i.e.
<include
layout="#layout/common_toolbar"
/>
which allows you to remove the CommonToolbar custom view
Use setContentInsetsAbsolute(0, 0);
public class CustomToolbar extends Toolbar {
#BindView(R.id.txt_screen_title)
TextView txtScreenTitle;
public CustomToolbar(Context context) {
super(context);
initToolbarLayout(context);
}
public CustomToolbar(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
initToolbarLayout(context);
}
public CustomToolbar(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initToolbarLayout(context);
}
private void initToolbarLayout(Context context) {
View toolbarView = LayoutInflater.from(context).inflate(R.layout.app_toolbar, null);
this.addView(toolbarView);
ButterKnife.bind(this, toolbarView);
setContentInsetsAbsolute(0, 0);
}
public void setScreenTitle(String title) {
if(txtScreenTitle != null) {
txtScreenTitle.setText(title);
}
}
}
I've got a custom view for my app named AvatarView:
<?xml version="1.0" encoding="utf-8"?>
<com.ulouder.views.AdvancedRelativeLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_gravity="center_horizontal"
android:layout_margin="0dp"
android:padding="0dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CP"
android:id="#+id/initialsView"
android:layout_alignTop="#+id/avatarView"
android:layout_alignLeft="#+id/avatarView"
android:layout_alignBottom="#+id/avatarView"
android:layout_alignRight="#+id/avatarView"
android:background="#drawable/avatar_background"
android:textColor="#color/white"
android:gravity="center"
android:textStyle="bold"
android:textSize="8sp" />
<com.makeramen.roundedimageview.RoundedImageView
app:riv_corner_radius="20dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/avatarView"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="4dp"
android:layout_marginLeft="4dp"
android:layout_marginBottom="4dp"
app:riv_border_color="#color/lightGray"
app:riv_border_width="0.2dp" />
</com.uLouder.views.AdvancedRelativeLayout>
AdvancedRelativeLayout is just a superclass of RelativeLayout with a small fix, nothing special there. Then, I've created a view that uses my custom view:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<com.ulouder.views.AvatarView
android:layout_width="50dp"
android:layout_height="50dp"/>
</LinearLayout>
Nothing fancy either. But in the designer view of the second layout XML, I'm getting this:
The editor displays my view hierarchy like it has a nested instance of itself, while clearly there isn't. If I delete either one, they both get deleted. If I declare attributes on one of them, other also gets it. They are clearly the same instance. The only exception is setting an ID. Then the problem disappears, and only single instance is displayed as expected.
I've rebuilt the project, restarted Android Studio, but it's still the same. What am I doing wrong?
UPDATE: Nope, now, after editing id, the problem still continues again.
UPDATE 2: It's not just a layout so I can't use <include> tag. It's a custom view which has custom logic inside.
UPDATE 3: Here is my custom view's (relevant) code:
public class AvatarView extends FrameLayout {
public AvatarView(Context context) {
super(context);
init();
}
TextView initialsView;
RoundedImageView imageView;
public AvatarView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
void init(){
inflate(getContext(), R.layout.view_avatar, this);
initialsView = (TextView) findViewById(R.id.innerInitialsView);
imageView = (RoundedImageView) findViewById(R.id.innerImageView);
}
#SuppressWarnings("SuspiciousNameCombination")
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec); //always square
imageView.setCornerRadius(widthMeasureSpec / 2f);
initialsView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, widthMeasureSpec * 30f);
}
}
UPDATE 4: It appears that this happens wherever I put my custom AvatarView class, not just at one place.
I did not find any reason to inflate the same view inside your class constructor method after checking the custom views documentation. Try to remove the inflate inside your init method.
...
public AvatarView(Context context) {
super(context);
init();
}
...
public AvatarView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
void init(){
// inflate(getContext(), R.layout.view_avatar, this);
initialsView = (TextView) findViewById(R.id.innerInitialsView);
imageView = (RoundedImageView) findViewById(R.id.innerImageView);
}
...
How do I add a hint on the top of a listView like "Pull down to refresh" which is contained in a swipeRefreshLayout from android.support.v4.
The pull down to refresh works but I want to add a text whenever the user pulls the listview slightly down.
EDIT 10/21/2014
If you update the support-v4 to the latest version (at least 21.0.0) you can use the built-in loading indicator!
I just came up with a simple, yet effective, solution.
The idea is to add a custom ViewGroup that grows its height when the SwipeRefreshLayout child gets pulled down. In this ViewGroup you will put everything you need for your hint (TextViews, ImageViews, ...).
I chose to extend a RelativeLayout because it makes easier to position your "hint" elements.
1) Create a custom widget as follows:
public class SwipeRefreshHintLayout extends RelativeLayout {
public SwipeRefreshHintLayout(Context context) {
super(context);
}
public SwipeRefreshHintLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SwipeRefreshHintLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setSwipeLayoutTarget(final SwipeRefreshLayout swipeRefreshLayout) {
final View swipeTarget = swipeRefreshLayout.getChildAt(0);
if (swipeTarget == null) {
return;
}
swipeTarget.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
private Rect oldBounds = new Rect(), newBounds = new Rect();
#Override
public boolean onPreDraw() {
newBounds.set(swipeTarget.getLeft(), swipeRefreshLayout.getTop(), swipeTarget.getRight(), swipeTarget.getTop());
if (!oldBounds.equals(newBounds)){
getLayoutParams().height = newBounds.height();
requestLayout();
oldBounds.set(newBounds);
}
return true;
}
});
}
}
2) In your Fragment or Activity layout use this custom widget.
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.widget.SwipeRefreshHintLayout
android:id="#+id/swipe_hint"
android:layout_width="match_parent"
android:layout_height="0dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="#string/label_swipe_to_refresh"/>
<!-- Any other view positioned using RelativeLayout rules -->
</com.example.widget.SwipeRefreshHintLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
3) Then, in your Activity onCreate() or in your Fragment onCreateView(), put those lines:
mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipe_container);
mSwipeRefreshHintLayout = (SwipeRefreshHintLayout) rootView.findViewById(R.id.swipe_hint);
mSwipeRefreshHintLayout.setSwipeLayoutTarget(mSwipeRefreshLayout);
Done!
I added a custom preference to my project (code below). I added it to my preferences xml with a custom widgetLayout:
<w.PlusOnePreference
android:title="Rate App"
android:key="custom"
android:widgetLayout="#layout/plusone_pref"/>
Preference layout xml:
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.gms.plus.PlusOneButton
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:plus="http://schemas.android.com/apk/lib/com.google.android.gms.plus"
android:id="#+id/plus_one_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:focusable="false"
plus:size="standard" />
I see the layout and the button in the layout works fine. The only problem is that the preference isn't clickable. Like it's hidden behind something.
Any ideas on how to make it clickable?
If I add a regular Preference (without a widget layout) it works fine.
Thanks.
public class PlusOnePreference extends Preference {
private PlusClient mPlusClient = null;
public PlusOnePreference(Context context) {
super(context);
}
public PlusOnePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PlusOnePreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setPlusClient(PlusClient plusClient) {
mPlusClient = plusClient;
}
#Override
protected void onBindView(View view) {
super.onBindView(view);
//mPlusOneButton =
PlusOneButton plusOneButton = (PlusOneButton)view.findViewById(R.id.plus_one_button);
plusOneButton.initialize(mPlusClient, SettingsActivity.URL, SettingsActivity.PLUS_ONE_REQUEST_CODE);
}
}
in layout/plusone_pref.xml set android:focusable="false" for your Button
Putting Pskink's answer together with Ran's comment:
If your custom preference's layout is a ViewGroup (e.g. a *Layout), use android:descendantFocusability="blocksDescendants"
If it's just one View, use android:focusable="false"
Preferences don't have a clickable attribute, though there is an onClick() method. Tryandroid:selectable.
I am learning to create a compound control in android.
For starters i tried an edit text with an attached button to clear it.
The problem is even though i can see the compound control in the graphical view of the
main.xml, there is an error message : "Custom view ClearableEditText is not using the 2- or 3-argument View constructors; XML attributes will not work"
This is not visible in project explorer under errors only in the xml graphical view
i am able to compile and run but get a force close.
XML : COMPOUND CONTROL clearable_edit_text.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<EditText android:id="#+id/editText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button android:id="#+id/clearButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CLEAR"
/>
</LinearLayout>
CLASS
public class ClearableEditText extends LinearLayout
{
EditText et;
Button btn;
public ClearableEditText(Context context)
{
super(context);
LayoutInflater li=(LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
li.inflate(R.layout.clearable_edit_text,this,true);
et=(EditText)findViewById(R.id.editText);
btn=(Button)findViewById(R.id.clearButton);
hookupButton();
}
private void hookupButton()
{
btn.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View v)
{
et.setText("");
}
});
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.commsware.android.merge.ClearableEditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<com.commsware.android.merge.ClearableEditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
Your class extends LinearLayout but you never add any views to it. You need to call addView(...) and pass your inflated view as the parameter.
Also, to define your view in XML you need to override the 2 and 3 argument constructors for a LinearLayout. Add this to your code:
public ClearableEditText(Context context, AttributeSet attrs) {
super( context, attrs );
}
public ClearableEditText(Context context, AttributeSet attrs, int defStyle) {
super( context, attrs, defStyle );
}
To get all 3 constructors to use the same initialization code, move your code from the single argument constructor to the 3 argument constructor, then in the other 2 constructors call this(context, null, 0) and this(context, attrs, 0) respectively.