I want to create a progress bar that automatically slides up when incremented. I've sorted the sliding bit (using java), but now want to create a custom progress bar component. This is what I have so far:
public class Smooth_Progress_Bar extends ProgressBar{
public Smooth_Progress_Bar(Context context) {
super(context);
}
}
I'm using this XML code:
<?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.todddavies.content.smoothprogressbar.Smooth_Progress_Bar
android:layout_width="fill_parent"
style="#android:style/Widget.ProgressBar.Horizontal"
android:layout_height="50dp"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:progressDrawable="#drawable/pb"/>
</LinearLayout>
I get an immediate force close :/
I strongly suspect I'm doing something majorly wrong with the java bit. I promise to tick the right answer!
I made a project using your code, with the exact same code (even the Object name "Smooth_Progress_Bar").
It all compiled and ran fine.
So this leaves:
You may need all three of the constructors, eg:
public Smooth_Progress_Bar(Context context) {
super(context);
}
public Smooth_Progress_Bar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public Smooth_Progress_Bar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
or your problem is in your progressDrawable. If you drawable is xml, it may contain errors. Try the constructors and if that doesn't work, examine your drawable.
Related
I'm trying to implement a simple custom dial pad view. It was all working fine until I tried to use <include> to include a "template" for my buttons instead of explicitly defining each button in the XML file. Since I'm gonna have 12 near-identical buttons I thought I'd just make my XML a bit neater and shorter by doing this.
The problem is that in onFinishInflate() in my custom view, when I call findViewById(R.id.dialpad_view_btn1) it returns null. I am of course assigning the id dialpad_view_btn1 to one of my <include>'s. If I simply define the button explicitly instead of including from my "template" it works fine. Is there any workaround for this, or should I just accept that findViewById() and <include> don't play well together?
DialPadView.java
public class DialPadView extends android.support.v7.widget.GridLayout {
public DialPadView(Context context) {
super(context);
initializeViews(context);
}
public DialPadView(Context context, AttributeSet attrs) {
super(context, attrs);
initializeViews(context);
}
public DialPadView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeViews(context);
}
private void initializeViews(Context context) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.dialpad_view, this);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
// This call to findViewById() results in a NullPointerException
((ImageButton) findViewById(R.id.dialpad_view_btn1)).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// I'm just a dummy
}
});
}
}
dialpad_view.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.GridLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.me.myproject.DialPadView"
xmlns:grid="http://schemas.android.com/apk/res-auto"
android:id="#+id/dialpad_grid_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
grid:alignmentMode="alignMargins"
grid:columnCount="3"
grid:rowCount="4">
<include
android:id="#+id/dialpad_view_btn1"
layout="#layout/dialpad_button" />
<include
android:id="#+id/dialpad_view_btn2"
layout="#layout/dialpad_button" />
</android.support.v7.widget.GridLayout>
dialpad_button.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:grid="http://schemas.android.com/apk/res-auto">
<Button
android:layout_width="0dp"
android:layout_height="0dp"
grid:layout_columnWeight="1"
grid:layout_rowWeight="1"
grid:layout_gravity="fill"
android:layout_margin="5dp"
android:scaleType="centerInside"
android:text="Test" />
</merge>
Problem with ids:
The problem you encountered is caused by mixing <include> and <merge> tags.
Please notice that <merge> serves different purpose and it should not be used in this case, so please use only <incluce> and make <Button> a root in your dialpad_button.xml file. Then all ids should be assigned properly.
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:grid="http://schemas.android.com/apk/res-auto">
android:layout_width="0dp"
android:layout_height="0dp"
grid:layout_columnWeight="1"
grid:layout_rowWeight="1"
grid:layout_gravity="fill"
android:layout_margin="5dp"
android:scaleType="centerInside"
android:text="Test" />
Details:
If you are curious about this problem you can check the parseInclude() method in LayoutInflater source file. You can find there a code handling <merge> inflation. You can see that the code for passing layout and id attributes to the child of <include> is not invoked for <merge> because <merge> should be used for different scenarios. there:
if (TAG_MERGE.equals(childName)) {
// Inflate all children.
rInflate(childParser, parent, childAttrs, false);
} else {
[...]
// We try to load the layout params set in the <include /> tag. If
// they don't exist, we will rely on the layout params set in the
// included XML file.
[...]
// Attempt to override the included layout's android:id with the
// one set on the <include /> tag itself.
TypedArray a = mContext.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.View, 0, 0);
int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID);
// While we're at it, let's try to override android:visibility.
[...]
}
Other problems:
It's not strictly related with your problem with missing ids, but as pskink mentioned you wrongly inflate <android.support.v7.widget.GridLayout> inside your custom DialPadView so you will end up with hierarchy like this:
<DialPadView>
<android.support.v7.widget.GridLayout>
<Button/>
<Button/>
</android.support.v7.widget.GridLayout>
</DialPadView>
but <DialPadView> is already an instance of android.support.v7.widget.GridLayout, so basically you end up having GridLayout inside of GridLayout. Apart from not being optimal it may result in layout looking in a different way than you've imagined it to look and behave.
I am making a custom layout, but it's not showing, and i do not know why.
Here is the XML file where the class is defined
<com.example.name.gw2applicaton.SpecializationView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="65dp"
android:layout_height="match_parent">
<Button
android:layout_width="50dp"
android:layout_height="50dp"
android:text="yop2" />
<Button
android:layout_width="50dp"
android:layout_height="50dp"
android:text="yop2" />
</LinearLayout>
</com.example.name.gw2applicaton.SpecializationView>
Here is the class, just a constructor
public class SpecializationView extends LinearLayout {
public SpecializationView(Context context) {
super(context);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.layout_specialization, this, true);
}
}
And finally where the class is used
<?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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<com.example.name.gw2applicaton.SpecializationView
android:id="#+id/view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical">
</com.example.name.gw2applicaton.SpecializationView>
</LinearLayout>
</LinearLayout>
The SpecializationView is not visible, I do not know why.
What am I doing wrong here?
That's not how it works for a custom view, as you are trying to do. Use this convention instead:
<?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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<!-- just include the layout you defined else where -->
<include layout="#layout/layout_specialization"/>
</LinearLayout>
Where layout_specialization.xml is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="65dp"
android:layout_height="match_parent">
<Button
android:layout_width="50dp"
android:layout_height="50dp"
android:text="yop2" />
<Button
android:layout_width="50dp"
android:layout_height="50dp"
android:text="yop2" />
</LinearLayout>
Note: You should use custom view definitions when you need to modify an existing view or viewgroup to have special programatic functionality, such as positioning, dynamic content, niche widget, etc... When you want to use a view like you are where it is just using existing widget functionality, do as I described. The include xml tag is great for defining an xml layout and re-using it through your project so there is a minimized duplication of code.
EDIT:
The reason you layout is not showing by the way is you have only defined the constructor for programmatically creating a view (via java code, not xml). To allow for an xml definition of your custom view extend the class as follows with the additional constructors needd:
public class SpecializationView extends LinearLayout {
/* Programmatic Constructor */
public SpecializationView(Context context) {
super(context);
init(context, null, 0);
}
/* An XML Constructor */
public SpecializationView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
/* An XML Constructor */
public SpecializationView(Context context, AttributeSet attrs, int resId) {
super(context, attrs, resId);
init(context, attrs, resId);
}
/**
* All initialization happens here!
*/
private void init(Context context, AttributeSet attrs, int resId){
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.layout_specialization, this, true);
}
}
This definition now includes the xml ability to create the custom view (which should now probably work for you). The reason it will work is now you send the attribute set, or the attributes definied via xml to the constructor. Since you didn't include it, it doesn't know what to do for your custom view when defined in xml and you cannot access the layout's attributes that you may define as custom.
I'm developing a music store where we show the albums in a grid. Each item is comprised of a cover album (which is a square image) and some text below it which shows artist name, album name, etc. This is the approximate result that I want:
Cover images are not uni-sized, some are 1000x1000, some are 500x500, and maybe there'll be some other sizes.
This is the current xml layout that I use for each item:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/cover"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="#+id/artistName"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Here is the important part of the RecyclerView's Adapter, the rest is typical stuff:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.artistName.setText(mAlbums.get(position).name);
Picasso.with(holder.cover.getContext())
.load(mAlbums.get(position).primaryImage)
.into(holder.cover);
}
I get this result:
The right picture is 500x500 and the other two are 1000x1000
However if I add some resize to the images
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.artistName.setText(mAlbums.get(position).name);
Picasso.with(holder.cover.getContext())
.load(mAlbums.get(position).primaryImage)
.resize(300, 300)
.centerInside()
.into(holder.cover);
}
I'll get a better result like:
300 is just a random number. I can replace it with SCREEN_WIDTH/3.
So.... Is there a better approach to tackle this problem (which I think is a very general problem in apps)?
use the following SquareRelativeLayout
package net.simplyadvanced.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
/** A RelativeLayout that will always be square -- same width and height,
* where the height is based off the width. */
public class SquareRelativeLayout extends RelativeLayout {
public SquareRelativeLayout(Context context) {
super(context);
}
public SquareRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquareRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(VERSION_CODES.LOLLIPOP)
public SquareRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Set a square layout.
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
}
look at the answer
Recyclerview - GridLayoutManager: Set square dimensions
you can do like this. on your activity/fragment.
private void setRecyclerView() {
productRecyclerView.setHasFixedSize(true);
final GridLayoutManager layoutManager = new GridLayoutManager(getActivity(), 3);
productRecyclerView.setLayoutManager(layoutManager);
recyclerViewAdapter = new ProductListAdapter(getActivity());
productRecyclerView.setAdapter(recyclerViewAdapter);
}
or you can achieve by adding different viewholders of different row/column heights.
Use a fixed height for item xml Like
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="100dp">
<ImageView
android:id="#+id/cover"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="#+id/artistName"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Hope this will solve the problem
I want to use Android's typical XML to define my layout but I need to override onScrollChanged() in ScrollView. Here is my current attempt which generates a class cast exception:
class MyHorizontalScrollView extends HorizontalScrollView {
private final AbstractChartActivity abstractChartActivity;
public MyHorizontalScrollView(AbstractChartActivity abstractChartActivity,
Context context) {
super(context);
this.abstractChartActivity = abstractChartActivity;
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
String tag = (String) this.getTag();
// if the instance is listening to the column header scroll, then
// move the body
if (tag.equalsIgnoreCase(AbstractChartActivity.COL_HEADER_SCROLL)) {
abstractChartActivity.goodiBodyHorizontalScrollView.scrollTo(l, 0);
} else {
// if the instance is listening to the body scroll, then move
// the header
abstractChartActivity.columnHeaderHorizontalScrollView.scrollTo(l, 0);
}
}
}
and the layout...
<TableLayout
android:id="#+id/blank_cell_above_labels_table"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</TableLayout>
<HorizontalScrollView
android:id="#+id/column_header_horizontal_scroll"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/blank_cell_above_labels_table" >
<TableLayout
android:id="#+id/column_header_table"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</TableLayout>
</HorizontalScrollView>
This line throws a class cast exception because MyHorizontalScrollView extends HorizontalScrollView but is not exactly the same type.
columnHeaderHorizontalScrollView = (HorizontalScrollView) findViewById(R.id.column_header_horizontal_scroll);
I have this all working by programmatically implementing the layout. But I worry it will be a maintenance nightmare so I want to shift functionality to XML where possible.
How can I extend an Android class and use it in XML?
Thanks
Use
<com.example.app.MyHorizontalScrollView
android:id="#+id/column_header_horizontal_scroll"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/blank_cell_above_labels_table" >
Where com.example.app is the package name where MyHorizontalScrollView is.
Then
columnHeaderHorizontalScrollView = (MyHorizontalScrollView) findViewById(R.id.column_header_horizontal_scroll);
Also add 2 more constructors
public MyHorizontalScrollView(Context context) {
super(context);
}
public MyHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
You may want to read a book my Retro Meir Professional Android Application Development. Chapter 4
When you scroll on Android using scrollview, it generates a blue light the direction you are scrolling in. How would I remove the blue light?
My manifest:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/sobreScrollView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#android:color/transparent"
android:scrollingCache="false"
android:fadingEdge="none"
android:fadingEdgeLength="0dp"
android:scrollbars="none"
android:scrollbarStyle="insideOverlay"
>
<LinearLayout
android:id="#+id/contentSobre"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
Java source code:
package com.my.app.section;
import android.content.Context;
import android.util.AttributeSet;
import com.my.app.BaseSection;
import com.my.app.R;
public class SobreSection extends BaseSection {
public SobreSection(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public SobreSection(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SobreSection(Context context) {
super(context);
}
#Override
protected void onFinishInflate() {
// TODO Auto-generated method stub
super.onFinishInflate();
findViewById(R.id.sobreScrollView).setVerticalFadingEdgeEnabled(false);
findViewById(R.id.sobreScrollView).setVerticalScrollBarEnabled(false);
}
}
Try adding this to your ScrollView in your layout.xml:
android:overScrollMode="never"
or add this to your code:
findViewById(R.id.sobreScrollView).setOverScrollMode(ScrollView.OVER_SCROLL_NEVER);
Add this extra line to your ScrollView definition:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/sobreScrollView"
...
android:overScrollMode="never">