I want to achieve something like this.
but instead of that, I'm getting something like this.
I'm using invalidate to redraw my custom view. But it is creating another view everytime the sides of polygon is changed. Where am I going wrong?
Here is my code.
PolygonView.Java
public class PolygonView extends View {
public float polygonRadius;
public int polygonSides;
public int polygonColor;
private Paint paint;
private Path path;
public PolygonView(Context context) {
super(context);
init(null);
}
public PolygonView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public PolygonView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public PolygonView(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
}
private void init(AttributeSet attrs)
{
polygonColor = ContextCompat.getColor(getContext(),R.color.polygonColor);
polygonSides = 5;
if (attrs!=null){
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs,R.styleable.PolygonView,0,0);
polygonColor = typedArray.getColor(R.styleable.PolygonView_polygon_color,polygonColor);
polygonRadius = typedArray.getDimension(R.styleable.PolygonView_polygon_radius,polygonRadius);
polygonSides = typedArray.getInteger(R.styleable.PolygonView_polygon_sides,polygonSides);
}
paint = new Paint();
paint.setColor(polygonColor);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
path = new Path();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
double angle = 2.0*Math.PI/polygonSides;
int cx = getWidth()/2;
int cy = getHeight()/2;
path.moveTo(
(float) (cx +polygonRadius*Math.cos(0.0)),
(float) (cy +polygonRadius*Math.sin(0.0))
);
for(int i=1;i<=polygonSides;i++){
path.lineTo(
(float) (cx + polygonRadius*Math.cos(angle*i)),
(float) (cy + polygonRadius*Math.sin(angle*i))
);
}
path.close();
canvas.drawPath(path,paint);
}
}
Fragment2.Java
public class Fragment2 extends Fragment {
public Fragment2() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_polygon, container, false);
final PolygonView polygonView = v.findViewById(R.id.polygonView);
SeekBar seekBarRadius = v.findViewById(R.id.seekBarRadius);
SeekBar seekBarSides = v.findViewById(R.id.seekBarSides);
seekBarSides.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
int p = progress/10;
if(p<1){
polygonView.polygonSides = 1;
}else if (p>10){
polygonView.polygonSides = 10;
}
else {
polygonView.polygonSides = p;
}
polygonView.invalidate();
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
return v;
}
}
You are always mutating your path but never reset it. Inside onDraw() method reset the path and only then apply new operations to it:
public void onDraw() {
path.reset();
...
}
Related
would try to move multiple points along an L shaped path to simulate water flow from a point to another. I created a view that allow to create a single point and created an animation. Now I'm stuck as I cannot understand how to retrieve values from the animator add points to it and update animation. Can someone try to move me on right path?
public class FlowAnimation extends View implements ValueAnimator.AnimatorUpdateListener {
int radius = 20;
int color = Color.WHITE;
Paint paint = null;
private ValueAnimator mAnimator;
PathMeasure pm;
float point[] = {0f, 0f};
Path path;
public FlowAnimation(Context context) {
super(context);
paint = new Paint();
path = new Path();
path.lineTo(100,150);
path.lineTo(300,500);
path.close();
}
public FlowAnimation(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
}
public FlowAnimation(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public FlowAnimation(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
super.onDraw(canvas);
int x = getWidth();
int y = getHeight();
paint.setColor(this.color);
canvas.drawCircle(x / 2, y / 2, this.radius, paint);
}
public void startAnim() {
// sets the range of our value
mAnimator = ValueAnimator.ofInt(0, 180);
// sets the duration of our animation
mAnimator.setDuration(1000);
// registers our AnimatorUpdateListener
mAnimator.addUpdateListener(this);
mAnimator.start();
}
#Override
public void onAnimationUpdate(#NonNull ValueAnimator valueAnimator) {
//gets the current value of our animation
int value = (int) valueAnimator.getAnimatedValue();
}
}
I have a custom control extending RadioGroup. I have a method to add custom radio button to RadioGroup (custom radio button has margins). So if i place my custom RadioGroup control inside HorizontalScrollView - it seems margins are lost. Then if soft keyboard shown - margins of radio button looks correct.
Screenshot of custom radiogroup control :
Here is a some code of my RadioGroup control
public class EDRadioGroup extends RadioGroup implements RadioGroup.OnCheckedChangeListener {
MarkerContainer mrk;
int markerContainerId;
DirectoryElement selectedElement;
public EDRadioGroup(Context context) {
super(context);
this.init(null);
}
public EDRadioGroup(Context context, AttributeSet attrs) {
super(context, attrs);
this.init(attrs);
}
private void init(#Nullable AttributeSet attributeSet) {
setOnCheckedChangeListener(this);
if (attributeSet != null) {
TypedArray a = this.getContext().getTheme().obtainStyledAttributes(attributeSet, R.styleable.ERadioGroup, 0, 0);
try {
if (a.hasValue(R.styleable.ERadioGroup_marker_container)) {
markerContainerId = a.getResourceId(R.styleable.ERadioGroup_marker_container, -1);
}
} finally {
a.recycle();
}
}
}
public void fillRadioGroup(List<DirectoryElement> items) {
if (items != null) {
for (DirectoryElement item : items) {
RadioButton rb = new YellowRadioButton(this.getContext());
rb.setText(String.format("%s%s", item.getTitle().substring(0, 1).toUpperCase(), item.getTitle().substring(1)));
rb.setTag(item);
this.addView(rb);
}
}
}
}
This is custom RadioButton code:
public class YellowRadioButton extends AppCompatRadioButton {
public YellowRadioButton(Context context) {
super(context);
this.init((AttributeSet)null);
}
public YellowRadioButton(Context context, AttributeSet attrs) {
super(context, attrs);
this.init(attrs);
}
public YellowRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.init(attrs);
}
private void init(#Nullable AttributeSet attributeSet) {
this.setBackground(ContextCompat.getDrawable(getContext(), R.drawable.radio_states_yellow));
this.setButtonDrawable(null);
this.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
this.setTextColor(ContextCompat.getColor(getContext(), R.color.dt_primary));
int padding = (int) ScreenDimensionsHelper.convertDpToPixel(10, getContext());
int minWidth = (int) ScreenDimensionsHelper.convertDpToPixel(80, getContext());
setPadding(padding, padding, padding, padding);
setMinWidth(minWidth);
setTextAlignment(TEXT_ALIGNMENT_CENTER);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
int marging = (int) ScreenDimensionsHelper.convertDpToPixel(8, getContext());
layoutParams.setMargins(marging, marging, marging, marging);
layoutParams.gravity = Gravity.CENTER;
setLayoutParams(layoutParams);
this.invalidate();
}
}
I'm new to Java and android development and I want to create this basic app, which consists of multiple CustomViews, where I could move different shapes drawn on canvas.
My idea is to have one class that handles MotionEvents and passes them to individual CustomViews.
My CustomView class:
public class CustomView extends View implements MoveDetector.OnMoveListener {
private MoveDetector mMoveDetector;
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public CustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
}
private void init(#Nullable AttributeSet set){
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.parseColor("#626696"));
}
#Override
public boolean onTouchEvent(MotionEvent event){
switch (event.getAction()){
case MotionEvent.ACTION_MOVE:
mMoveDetector = new MoveDetector(this);
mMoveDetector.onTouchEvent(event);
float cx = mMoveDetector.getCx();
float cy = mMoveDetector.getCy();
Toast.makeText(getContext(),"why u packin heat bro?"+Float.toString(cx)+" "+Float.toString(cy),Toast.LENGTH_SHORT).show();
Log.d("Moveee", "Move: " + Float.toString(cx)+" " + Float.toString(cy));
}
return super.onTouchEvent(event);
}
#Override
public void OnMove(MoveDetector moveDetector) {
}
}
My MoveDetector Class:
public class MoveDetector {
private float cx,cy, mCislo1,mCislo2;
public float getCx() {
return mCislo1;
}
public float getCy() {
return mCislo2;
}
private OnMoveListener mListener;
public MoveDetector(OnMoveListener listener){
mListener = listener;
}
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
float x = event.getX();
float y = event.getY();
mCislo1 = cXX(x);
mCislo2 = cYY(y);
if (mListener != null) {
mListener.OnMove(this);
}
break;
}
return true;}
public static interface OnMoveListener {
public void OnMove(MoveDetector moveDetector);
}
public float cXX(float x){
cx = x;
return cx;
}
public float cYY(float y){
cy = y;
return cy;
}
}
It doesn't seem to be working and I can't point why.
I searched a solution for ages and couldn't find any.
So I'd be really thankful if someone could help me out.
P.S.: Sorry, if my code is messy: I'm still learning.
I've tried extending LinearLayout instead of simple View for adding buttons and working children of View but I am still not getting any output. Conceptually I'm wrong somewhere.
public class TouchEventView extends LinearLayout {
private Paint paint = new Paint();
private Path path = new Path();
private ViewGroup viewGroup;
public TouchEventView(Context ctx) {
super(ctx);
//Button Code Starts Here
LinearLayout touch = new TouchEventView(ctx);
Button bt = new Button(ctx);
bt.setText("A Button");
bt.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT));
touch.addView(bt); //Button Not Working
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5f);
this.setBackgroundColor(Color.WHITE);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path,paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float xPos = event.getX();
float yPos = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(xPos,yPos);
return true;
case MotionEvent.ACTION_MOVE:
path.lineTo(xPos,yPos);
break;
case MotionEvent.ACTION_UP:
break;
default:
return false;
}
invalidate();
return true;
}
}
The issue is that you are creating a new TouchEventView and adding the Button to that View. Instead you should add the Button directly to the current View.
You should also implement the other constructors from LinearLayout if you want to be able to get any attributes from XML.
public class TouchEventView extends LinearLayout {
public TouchEventView(Context context) {
this(context, null);
}
public TouchEventView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TouchEventView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
Button button = new Button(getContext());
button.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
addView(button);
}
}
I have a custom view extends RelativeLayout, and I want to add a RecyclerView in it. But the recyclerView can't be shown.
How to solve it? Thanks!
The code:
LineChart.java
public class LineChart extends RelativeLayout {
RecyclerView mRecyclerView;
public LineChart(Context context) {
super(context);
init(context, null);
}
public LineChart(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public LineChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
protected void init(Context context, #Nullable AttributeSet attrs) {
setWillNotDraw(false);
initInside();
}
protected void initInside() {
View v = inflate(getContext(), R.layout.line_chart_recyclerview, null);
LineChart.LayoutParams params = new LineChart.LayoutParams(
getWidth() / 2, getHeight());
params.addRule(LineChart.ALIGN_PARENT_RIGHT);
addView(v, params);
mRecyclerView = (RecyclerView) findViewById(R.id.lineChartRecyclerView);
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");
list.add("~~~");
LineChartAdapter lineChartAdapter = new LineChartAdapter(getContext(),
list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mRecyclerView.setAdapter(lineChartAdapter);
}
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
LineChartAdapter.java
public class LineChartAdapter
extends RecyclerView.Adapter<LineChartViewHolder> {
private List<String> mList;
private Context mContext;
public LineChartAdapter(Context context, List<String> list) {
this.mContext = context;
this.mList = list;
}
#Override
public LineChartViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(mContext)
.inflate(R.layout.item_line_chart, parent,
false);
return new LineChartViewHolder(itemView);
}
#Override
public void onBindViewHolder(LineChartViewHolder holder, int position) {
holder.mTextView.setText(String.valueOf(position));
}
#Override public int getItemCount() {
return mList == null ? 0 : mList.size();
}
}
LineChartViewHolder.java
public class LineChartViewHolder extends RecyclerView.ViewHolder {
protected TextView mTextView;
public LineChartViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(
R.id.text1024);
}
}
I think, I know the reason.
View v = inflate(getContext(), R.layout.line_chart_recyclerview, null);
LineChart.LayoutParams params = new LineChart.LayoutParams(
getWidth() / 2, getHeight());
params.addRule(LineChart.ALIGN_PARENT_RIGHT);
addView(v, params);
Here, getWidth() and getHeigth() are still 0. So your view will have (0;0) size
To fix this, I'd suggest to replace this part with
LayoutInflater.from(context).inflate(R.layout.line_chart_recyclerview, this);
Let me know, if it helps.