I have a fairly simple question which somehow I can't figure out. I'm using a FrameLayout with an own view (onDrawn is overriden) and another transparent view which extends LinearLayout. I want to add scrolling for the transparent view, but if I use ScrollView in the XML a Classcast exception is thrown.
My alternative was to implement scrolling on my own (e.g. with scrollTo in LinearLayout, where I can't find any example using that method), but the OnGestureListener doesn't trigger onScroll, while onShowPress and onLongPress are triggered. I then tried to use onTouchEvent in the LinearLayout, but it only recognises ACTION_DOWN, not ACTION_MOVE. In my own view all that works perfectly.
Here the XML:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/home_container"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.unimelb.pt2.ui.WaterfallView
android:id="#+id/waterfall_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:apiKey="0DUEIIn35xtmfWC2DXprK5kqNF-aEaNgRJ4ONxw"/>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:gravity="bottom"
android:paddingLeft="0px"
android:paddingTop="0px"
android:paddingRight="0px">
<com.unimelb.pt2.ui.TransparentPanel
android:id="#+id/workbench"
android:layout_width="fill_parent"
android:layout_height="10px"
android:paddingTop="0px"
android:paddingLeft="0px"
android:paddingBottom="0px"
android:paddingRight="0px">
</com.unimelb.pt2.ui.TransparentPanel>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:gravity="right"
android:paddingLeft="0px"
android:paddingTop="0px"
android:paddingRight="0px">
<com.unimelb.pt2.ui.TransparentPanel
android:id="#+id/tagarea"
android:layout_width="50px"
android:layout_height="fill_parent"
android:paddingTop="0px"
android:paddingLeft="0px"
android:paddingBottom="0px"
android:paddingRight="0px">
</com.unimelb.pt2.ui.TransparentPanel>
</LinearLayout>
</FrameLayout>
Here the basic construct of the WaterfallView:
public class WaterfallView extends View {
private GestureDetector gestureScanner;
private Vector<PictureEntry> allPictures = new Vector<PictureEntry>();
public WaterfallView(Context context) {
super(context);
this.initialize(context);
}
public void initialize(Context context) {
this.setFocusable(true);
this.setClickable(true);
this.context = context;
allPictures.add(new PictureEntry(context, R.drawable.sample_0));
allPictures.add(new PictureEntry(context, R.drawable.sample_1));
allPictures.add(new PictureEntry(context, R.drawable.sample_2));
allPictures.add(new PictureEntry(context, R.drawable.sample_3));
allPictures.add(new PictureEntry(context, R.drawable.sample_4));
allPictures.add(new PictureEntry(context, R.drawable.sample_5));
allPictures.add(new PictureEntry(context, R.drawable.sample_6));
allPictures.add(new PictureEntry(context, R.drawable.sample_7));
}
public void setGestureDetector(GlassPane gp) {
gestureScanner = new GestureDetector(context, gp);
}
#Override
protected void onDraw(Canvas canvas) {
Iterator<PictureEntry> iter = allPictures.iterator();
int i = 0;
while (iter.hasNext()) {
PictureEntry pic = iter.next();
pic.draw(canvas)
}
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (gestureScanner.onTouchEvent(event)) {
return Prototype.glass.pictureTouch(event);
} else return false;
}
}
Here the basic construct of the GlassPane:
public class GlassPane implements OnGestureListener {
public GlassPane(WaterfallView waterfall) {
super();
waterfall.setGestureDetector(this);
}
public boolean pictureTouch(MotionEvent event) {
// Handles drag and drop and zoom pinch
}
public boolean onDown(MotionEvent e) {
Log.i("Test", "DOWN");
return false;
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
Log.i("Test", "FLING");
return false;
}
#Override
public void onLongPress(MotionEvent e) {
Log.i("Test", "LONG PRESS");
}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
Log.i("Test", "SCROLL");
return true;
}
#Override
public void onShowPress(MotionEvent e) {
Log.i("Test", "SHOW PRESS");
}
}
And here the construct of the TransparentPanel:
public class TransparentPanel extends LinearLayout {
private Paint innerPaint, borderPaint;
private int width, height, scrollOffset;
private Context mContext;
public TransparentPanel(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
init();
}
public TransparentPanel(Context context) {
super(context);
init();
}
private void init() {
innerPaint = new Paint();
innerPaint.setARGB(225, 75, 75, 75); // gray
innerPaint.setAntiAlias(true);
}
public void setDimension(int w, int h) {
width = w; height = h;
this.setLayoutParams(new LayoutParams(width, height));
this.invalidate();
}
#Override
protected void dispatchDraw(Canvas canvas) {
RectF drawRect = new RectF();
drawRect.set(0, 0, width, height);
canvas.drawRect(drawRect, innerPaint);
super.dispatchDraw(canvas);
}
private void measure() {
if(this.getOrientation()==LinearLayout.VERTICAL) {
int h = 0;
for(int i=0; i<this.getChildCount(); i++) {
View v = this.getChildAt(i);
h += v.getMeasuredHeight();
}
height = (h < height) ? height : h;
Log.d(Prototype.TAG, "mW:"+width+", mH:"+height);
}
this.setMeasuredDimension(width, height);
}
}
Okay, I think I finally figured everything out:
The ClassCastException was thrown because in my TransparentPanel I try to assign LayoutParams to the panel without stating which kind of LayoutParams. I thought it should be LinearLayout.LayoutParams but in fact I need to assign the LayoutParams of the ViewGroup in which I place the View, i.e. RelativeLayout in my case.
My GlassPanel is placed better on the bottom of the FrameLayout rather than on the top. The MotionEvents are than passed down from top to bottom as expected. I just start with whatever is lying on top and if the event is not handled by that layer I return false and pass down the event to the next layer rather than having a real GlassPane on top.
In order to handle the event in the GlassPane on top of the FrameLayout I just need to override the onTouchEvent method in all the Views that use the GlassPane as EventListener. Just like in WaterfallView in the above code. But careful MotionEvent.getX() and MotionEvent.getY() return values relative to that View and not absolute. Solution (2) works perfect relatively to the GlassPane.
Related
My Issue is that I have a LinearLayout that I am inflating at runtime to a LinearLayout inside ScrollView.
This is the main_activity.xml
<ScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/controlLayoutCV"
android:layout_alignParentStart="true"
android:layout_below="#+id/toolLayoutCV">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="#dimen/dp5"
android:paddingLeft="#dimen/dp7"
android:paddingRight="#dimen/dp7"
android:paddingTop="#dimen/dp5">
<android.support.v7.widget.CardView
android:id="#+id/questionQuizCV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardElevation="#dimen/dp2">
<com.emedicoz.app.CustomViews.JustifiedTextView
android:id="#+id/questionQuizTV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="#dimen/dp5"
android:text="" />
</android.support.v7.widget.CardView>
<LinearLayout
android:id="#+id/quizQuestionLL"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/questionQuizCV"
android:layout_marginTop="#dimen/dp5"
android:orientation="vertical"
android:padding="#dimen/dp5" />
</RelativeLayout>
</ScrollView>
& this is the item_layout.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mcqlayout_LL"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="#dimen/dp5"
android:minHeight="30dp"
android:orientation="horizontal"
android:paddingBottom="#dimen/dp7"
android:paddingTop="#dimen/dp7">
<TextView
android:id="#+id/optioniconTV"
android:layout_width="#dimen/dp40"
android:layout_height="#dimen/dp40"
android:background="#drawable/circle_bg"
android:gravity="center"
android:padding="#dimen/dp3"
android:text="A"
android:textSize="#dimen/sub_heading_text_size" />
<com.emedicoz.app.CustomViews.JustifiedTextView
android:id="#+id/optionTextTV"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="#dimen/dp10" />
</LinearLayout>
This is the CustomTextView that I have created to show the HTML content directly. The JustifiedTextView.class is
public class JustifiedTextView extends WebView {
private String text = "";
private int textSize = 12;
private int backgroundColor = Color.TRANSPARENT;
public JustifiedTextView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setWebChromeClient(new WebChromeClient() {});
}
public void setText(String s) {
this.text = s;
reloadData();
}
#SuppressLint("NewApi")
private void reloadData() {
// loadData(...) has a bug showing utf-8 correctly. That's why we need to set it first.
this.getSettings().setDefaultTextEncodingName("utf-8");
// this.loadData(String.format(core,textColor,textSize,text), "text/html","utf-8");
this.loadData(text, "text/html", "utf-8");
// set WebView's background color *after* data was loaded.
super.setBackgroundColor(backgroundColor);
// Hardware rendering breaks background color to work as expected.
// Need to use software renderer in that case.
if (android.os.Build.VERSION.SDK_INT >= 11)
this.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
public void setTextSize(int textSize) {
this.textSize = textSize;
reloadData();
}
}
I have tried All of the solutions mentioned below.
Disable WebView touch events in Android
Already tried to set the android:descendantFocusability="blocksDescendants" to ScrollView
Why the click Event of LinearLayout does not fire when making click on WebView?
This is the way I am inflating the Layout and handle the click event.
private LinearLayout initAnswerMCViews(String text, String questions, Questions questionsModel) {
LinearLayout view = (LinearLayout) View.inflate(activity, R.layout.mcq_quiz, null);
answerTV = (JustifiedTextView) view.findViewById(R.id.optionTextTV);
optionIconTV = (TextView) view.findViewById(R.id.optioniconTV);
mcqItemLL = (LinearLayout) view.findViewById(R.id.mcqlayout_LL);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
lp.setMargins(3, 3, 3, 3);
mcqItemLL.setLayoutParams(lp);
if (questionsModel.isAnswered()) {
String[] answer = questionsModel.getUser_answer().split(",");
for (int i = 0; i < answer.length; i++) {
if (answer[i].equals(text)) {
answerTV.setText(questions);
optionIconTV.setText(text);
optionIconTV.setBackgroundResource(R.drawable.circle_bg_true);
} else {
answerTV.setText(questions);
optionIconTV.setText(text);
}
}
} else {
answerTV.setText(questions);
optionIconTV.setText(text);
}
mcqItemLL.setTag(R.id.questions, optionIconTV.getText().toString());
mcqItemLL.setTag(R.id.optionsAns, mcqItemLL);
mcqItemLL.setOnClickListener(optionClickListener);
viewArrayList.add(mcqItemLL);
return view;
}
Why the click is not get listen when clicked on the WebView part in the Layout?
I finally found the issue.
It is basically the issue of conflicting Touch Event of both parent Scrollview and CustomWebView.
So, by using the new classes that itself overriding the ClickListener and TouchListener.
package com.app.CustomViews;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
public class TestSeriesOptionWebView extends WebView implements GestureDetector.OnGestureListener {
private boolean check = false;
class LongClick implements OnLongClickListener {
final /* synthetic */ TestSeriesOptionWebView testSeriesOptionWebView;
LongClick(TestSeriesOptionWebView testSeriesOptionWebView) {
this.testSeriesOptionWebView = testSeriesOptionWebView;
}
public boolean onLongClick(View view) {
return true;
}
}
public TestSeriesOptionWebView(Context context) {
super(context);
handleClick();
}
public TestSeriesOptionWebView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
handleClick();
}
public TestSeriesOptionWebView(Context context, AttributeSet attributeSet, int i) {
super(context, attributeSet, i);
handleClick();
}
private void handleClick() {
setFocusable(false);
/*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
setLayerType(View.LAYER_TYPE_HARDWARE, null);
} else {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}*/
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
setVerticalScrollBarEnabled(false);
setBackgroundColor(0);
setHapticFeedbackEnabled(false);
setOnLongClickListener(new LongClick(this));
}
public void setDisableWebViewTouchListener(boolean z) {
this.check = z;
}
public boolean onTouchEvent(MotionEvent motionEvent) {
if (this.check) {
return false;
}
return super.onTouchEvent(motionEvent);
}
public boolean canScrollHorizontal(int i) {
int computeHorizontalScrollOffset = computeHorizontalScrollOffset();
int computeHorizontalScrollRange = computeHorizontalScrollRange() - computeHorizontalScrollExtent();
if (computeHorizontalScrollRange == 0) {
return false;
}
if (i < 0) {
if (computeHorizontalScrollOffset <= 0) {
return false;
}
return true;
} else if (computeHorizontalScrollOffset >= computeHorizontalScrollRange - 1) {
return false;
} else {
return true;
}
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
public boolean onDown(MotionEvent motionEvent) {
return true;
}
public void onShowPress(MotionEvent motionEvent) {
}
public boolean onSingleTapUp(MotionEvent motionEvent) {
return false;
}
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent2, float f, float f2) {
return f != 0.0f;
}
public void onLongPress(MotionEvent motionEvent) {
}
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent2, float f, float f2) {
return true;
}
}
If anybody faces this type of issue, then Use this Custom Class TestSeriesOptionWebView.class.
link check this news app, i want to develop like this.
i want swipe up/down side effect..
i tried this code,but not correctly swiping as i want.
in this code when i swipe up/down,only the text is changing not a layout.
public class ArticlesActivity extends Activity implements GestureDetector.OnGestureListener{
ImageView image,imageArticle;
TextView tv1,tv2,tv3;
private GestureDetector gd;
LinearLayout layout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_articles);
//Creating GestureDetector Object
gd = new GestureDetector(this, this);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
//Registering TouchEvent with GestureDetector
return gd.onTouchEvent(event);
}
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
//Destroying Activity
finish();
}
#Override
public boolean onDown(MotionEvent arg0) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// TODO Auto-generated method stub
//Defining Sensitivity
float sensitivity = 50;
//Swipe Up Check
if(e1.getY() - e2.getY() > sensitivity){
//Setting Image Resource to Up_Arrow on Swipe Up
tv1.setText("Some Text");
tv2.setText("Some Text");
tv3.setText("Some Text");
image.setImageResource(R.drawable.logo);
imageArticle.setImageResource(R.drawable.ic_launcher);
return true;
}
//Swipe Down Check
else if(e2.getY() - e1.getY() > sensitivity){
//Setting Image Resource to Down_Arrow on Swipe Down
tv1.setText("Some Text");
tv2.setText("Some Text");
tv3.setText("Some Text");
image.setImageResource(R.drawable.ic_launcher);
imageArticle.setImageResource(R.drawable.logo);
return true;
}
else{
//If some error occurrs, setting again to Default_Image (Actually it will never happen in this case)
image.setImageResource(R.drawable.logo);
return true;
}
}
#Override
public void onLongPress(MotionEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2,
float arg3) {
// TODO Auto-generated method stub
return false;
}
#Override
public void onShowPress(MotionEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public boolean onSingleTapUp(MotionEvent arg0) {
// TODO Auto-generated method stub
return false;
}
}
This will work like inshorts app
main.xml
<com.cardviewanimation.VerticalViewPager
android:id="#+id/verticleViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
android:id="#+id/card_view"
android:layout_gravity="center"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
card_view:cardCornerRadius="2dp"
card_view:contentPadding="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="300dp"
android:src="#drawable/background"
android:scaleType="fitXY"/>
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"/>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
VerticleViewPagerActivity.java
public class VerticleViewPagerActivity extends AppCompatActivity {
VerticalViewPager verticalViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
verticalViewPager = (VerticalViewPager) findViewById(R.id.verticleViewPager);
verticalViewPager.setAdapter(new VerticlePagerAdapter(this));
}
}
VerticlePagerAdapter.java
public class VerticlePagerAdapter extends PagerAdapter {
String mResources[] = {"To start off lets understand what exactly Android CardView is? Its a new widget for Android, which can be used to display a card sort of a layout in android. As you may know Android material design is inspired from paper and ink concept. Mostly it displays views on top of each other, with shadows. In simple terms, Android CardView is such a view which has all material design properties, most importantly showing shadows according the elevation. The best part about this view is that it extends FrameLayout and it can be displayed on all the platforms of android since it’s available through the Support v7 library. Lets have a look at some of its properties:","To start off lets understand what exactly Android CardView is? Its a new widget for Android, which can be used to display a card sort of a layout in android. As you may know Android material design is inspired from paper and ink concept. Mostly it displays views on top of each other, with shadows. In simple terms, Android CardView is such a view which has all material design properties, most importantly showing shadows according the elevation. The best part about this view is that it extends FrameLayout and it can be displayed on all the platforms of android since it’s available through the Support v7 library. Lets have a look at some of its properties:"};
Context mContext;
LayoutInflater mLayoutInflater;
public VerticlePagerAdapter(Context context) {
mContext = context;
mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mResources.length;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((LinearLayout) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
View itemView = mLayoutInflater.inflate(R.layout.content_main, container, false);
TextView label = (TextView) itemView.findViewById(R.id.textView);
label.setText(mResources[position]);
container.addView(itemView);
return itemView;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((LinearLayout) object);
}
}
VerticalViewPager.java
public class VerticalViewPager extends ViewPager {
public VerticalViewPager(Context context) {
super(context);
init();
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// The majority of the magic happens here
setPageTransformer(true, new VerticalPageTransformer());
// The easiest way to get rid of the overscroll drawing that happens on the left and right
setOverScrollMode(OVER_SCROLL_NEVER);
}
private class VerticalPageTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.75f;
#Override
public void transformPage(View view, float position) {
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(view.getWidth() * -position);
//set Y position to swipe in from top
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
view.setScaleX(1);
view.setScaleY(1);
} else if (position <= 1) { // [0,1]
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(view.getWidth() * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
/**
* Swaps the X and Y coordinates of your touch event.
*/
private MotionEvent swapXY(MotionEvent ev) {
float width = getWidth();
float height = getHeight();
float newX = (ev.getY() / height) * width;
float newY = (ev.getX() / width) * height;
ev.setLocation(newX, newY);
return ev;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev){
boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
swapXY(ev); // return touch coordinates to original reference frame for any child views
return intercepted;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapXY(ev));
}
}
I got the solution for your problem as inshorts app using vertical view pager. I am sharing some code that can serve your purpose.
public class VerticalViewPager extends ViewPager {
public VerticalViewPager(Context context) {
this(context, null);
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
setPageTransformer(false, new DefaultTransformer());
}
private MotionEvent swapTouchEvent(MotionEvent event) {
float width = getWidth();
float height = getHeight();
float swappedX = (event.getY() / height) * width;
float swappedY = (event.getX() / width) * height;
event.setLocation(swappedX, swappedY);
return event;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercept = super.onInterceptTouchEvent(swapTouchEvent(event));
//If not intercept, touch event should not be swapped.
swapTouchEvent(event);
return intercept;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapTouchEvent(ev));
}
}
As you can see the class above uses the DefaultTransformer class for transformation which implement ViewPager.PageTransformer for custom animation this class code is given below
public class DefaultTransformer implements ViewPager.PageTransformer {
#Override
public void transformPage(View view, float position) {
float alpha = 0;
if (0 <= position && position <= 1) {
alpha = 1 - position;
} else if (-1 < position && position < 0) {
alpha = position + 1;
}
System.out.println("alpha--" + alpha);
view.setAlpha(alpha);
System.out.println("position--" + position);
System.out.println("view.getWidth()--" + view.getWidth());
view.setTranslationX(view.getWidth() * -position);
float yPosition = position * view.getHeight();
System.out.println("yPosition---"+yPosition);
view.setTranslationY(yPosition);
}
}
and my activity code is like this
public class SwipeUpActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_swipe_layout);
setTitle("");
initViewPager();
}
private void initViewPager() {
VerticalViewPager viewPager = (VerticalViewPager) findViewById(R.id.vertical_viewpager);
//viewPager.setPageTransformer(false, new ZoomOutTransformer());
//viewPager.setPageTransformer(true, new StackTransformer());
String title = "ContentFragment";
viewPager.setAdapter(new ContentFragmentAdapter.Holder(getSupportFragmentManager())
.add(ContentFragment.newInstance(title, 1))
.add(ContentFragment.newInstance(title, 2))
.add(ContentFragment.newInstance(title, 3))
.add(ContentFragment.newInstance(title, 4))
.add(ContentFragment.newInstance(title, 5))
.set());
//If you setting other scroll mode, the scrolled fade is shown from either side of display.
viewPager.setOverScrollMode(View.OVER_SCROLL_NEVER);
}
The layout named activity_swipe_layout is like this
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#A6000000"
android:layout_height="match_parent">
<gmaillogindemo.com.irk.transforms.VerticalViewPager
android:id="#+id/vertical_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
and the result is looking like this:
after swipe up
what I want to do is to add a custom view to my activity and freely drag and drop it wherever I want to. I create a new class "Square". When I construct a new Square it is displayed in the upper left Corner of the Display. When I set X and Y coordinates it disappears. If I set X and Y to 30 I see that the Square is displayed partiatelly. So it seems that my view is only displayed in a small Frame on the top Left of the Display.
Please help me to move my custom view oder the Display. Thanks!
MainActivity:
private Button btnNewSquare;
private RelativeLayout relativeLayout;
private Square[] square = new Square[10];
int squareId = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnNewSquare = (Button)findViewById(R.id.button1);
relativeLayout = (RelativeLayout)findViewById(R.id.RelativeLayout);
btnNewSquare.setOnClickListener(this);
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(v == btnNewSquare) {
squareId++;
square[squareId] = new Square(this);
square[squareId].setX(30);
square[squareId].setY(30);
square[squareId].setOnTouchListener(square[squareId]);
relativeLayout.addView(square[squareId]);
relativeLayout.invalidate();
Toast.makeText(getApplication(), "Square" + squareId, Toast.LENGTH_SHORT).show();
square[squareId].invalidate();
}
}
Square
private int squareWidth = 100;
private Paint paint;
public Square(Context context) {
super(context);
initSquare();
// TODO Auto-generated constructor stub
}
public Square(Context context, AttributeSet attrs) {
super(context, attrs);
initSquare();
}
private final void initSquare() {
paint = new Paint();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(squareWidth, squareWidth);
}
#Override
protected void onDraw(Canvas canvas) {
//super.onDraw(canvas);
paint.setStyle(Style.FILL);
paint.setColor(Color.BLACK);
paint.setStrokeWidth(3);
canvas.drawRect(getX(), getY(), getX() + squareWidth, getY() + squareWidth, paint);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
v.setVisibility(View.VISIBLE);
}
return false;
}
My 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:id="#+id/RelativeLayout"
tools:context="${relativePackage}.${activityClass}" >
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="38dp"
android:text="New Square" />
</RelativeLayout>
The answer is simple. The coordinates in the onDraw() method are belonging to the view, not to the Screen. So if I move my view via Drag and Drop to let's say (500, 500) I have to draw my rectangle in the onDraw method still at (0,0,100,100) and not at (500, 500, 600, 600) so the correct line of code would be:
canvas.drawRect(0, 0, squareWidth, squareWidth, paint);
and not
canvas.drawRect(getX(), getY(), getX() + squareWidth, getY() + squareWidth, paint);
This is my xml file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/LinearLayout0"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CanvasActivity"
android:baselineAligned="false">
<LinearLayout
android:id="#+id/LinearLayout1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#f9dfcb">
</LinearLayout>
<LinearLayout
android:id="#+id/LinearLayout2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="3"
android:background="#000000"
>
</LinearLayout>
</LinearLayout>
My activity code
import android.app.Activity;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.LinearLayout;
import android.widget.Toast;
public class CanvasActivity extends Activity {
private GestureDetector gestureDetector;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_canvas);
gestureDetector = new GestureDetector(this, new GestureListener());
LinearLayout LinearLayout1=(LinearLayout)findViewById(R.id.LinearLayout1);
LinearLayout1.setOnTouchListener(new OnTouchListener(){
public boolean onTouch(View v, MotionEvent event){
if (gestureDetector.onTouchEvent(event))
{
return true;
}
return false;
}
});
/*********EDITED*******/
LinearLayout LinearLayout2=(LinearLayout)findViewById(R.id.LinearLayout2);
int width=LinearLayout2.getWidth();
int height=LinearLayout2.getHeight();
LinearLayout2.addView(new Canvasview(this), width, height) ;
LinearLayout2.setOnTouchListener(new OnTouchListener(){
public boolean onTouch(View v, MotionEvent event){
Canvasview cv= new Canvasview(v.getContext());
cv.onTouchEvent(event);
return true;
}
});
}
//discerning swipes
class GestureListener extends SimpleOnGestureListener
{
private static final int SWIPE_MIN_DISTANCE = 120;
private static final int SWIPE_THRESHOLD_VELOCITY = 200;
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY)
{
if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE &&
Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
//From Right to Left
Toast.makeText(CanvasActivity.this, "Left Swipe", Toast.LENGTH_SHORT).show();
return true;
}
else if(e1.getY() - e2.getY() > SWIPE_MIN_DISTANCE &&
Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
//From Bottom to Top
Toast.makeText(CanvasActivity.this, "Top Swipe", Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
#Override
public boolean onDown(MotionEvent e)
{
return true;
}
}
}
My Canvasview looks like this
public class Canvasview extends SurfaceView implements SurfaceHolder.Callback{
private int width, height;
private Paint textPaint = new Paint();
private Paint touchPaint = new Paint();
private int colors[] = new int[10];
public Canvasview(Context context) {
super(context);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
setFocusable(true); // make sure we get key events
setFocusableInTouchMode(true); // make sure we get touch events
init();
}
private void init()
{
textPaint.setColor(Color.WHITE);
colors[0] = Color.RED;
touchPaint = new Paint();
touchPaint.setColor(colors[0]);
}
public boolean onTouchEvent(MotionEvent event)
{
Canvas c = getHolder().lockCanvas();
if (c != null)
{
if (event.getAction() == MotionEvent.ACTION_UP)
{
// clear everything
}
else
{
int xval = (int)event.getX();
int yval = (int)event.getY();
drawCircle(xval,yval,touchPaint,c);
}
getHolder().unlockCanvasAndPost(c);
}
return true;
}
public void clear(Canvas c){
c = getHolder().lockCanvas();
c.drawColor(Color.BLACK);
getHolder().unlockCanvasAndPost(c);
}
private void drawCircle(int x, int y, Paint paint, Canvas c)
{
c.drawCircle(x, y, 2, paint);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){
Canvas c = getHolder().lockCanvas();
if (c != null)
{
// clear screen
c.drawColor(Color.BLACK);
getHolder().unlockCanvasAndPost(c);
}
}
#Override
public void surfaceCreated(SurfaceHolder arg0) {
// TODO Auto-generated method stub
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
// TODO Auto-generated method stub
}
}
I need the canvas only in linearlayout2, but the canvas occupies the entire screen. I am not sure where I am going wrong.
EDIT:
Well I did few changes and I get the Canvasview in linearlayout2, the ontouchevents of Canvasview detects the touch events but in Canvasview public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) method does not initialize a canvas and therefore it is always null. To draw I need the canvas not to be null. Any help is appreciated
set hardcode value of layout_width for first linear layout and try again
The first problem is that you want to get height and width of a view, that hasn't been drawn yet in onCreate():
int width=LinearLayout2.getWidth();
int height=LinearLayout2.getHeight();
A possible solution for that is to set static values or use a ViewTreeObserver (https://stackoverflow.com/search?q=get+measured+width+height+android).
The other problem you might have is that linear layout doesn't call onTouch() method. You may try using a scrolling view(scrollview for example) instead of linear layout.
I changed my xml file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/LinearLayout0"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".CanvasActivity"
android:baselineAligned="false">
<View
android:id="#+id/view0"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="3"
android:background="#f9dfcb"
/>
<com.multitel.testwidget.Canvasview
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/surfaceView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
</com.multitel.testwidget.Canvasview>
</LinearLayout>
and changed the constructor in Canvasview to
public Canvasview(Context context, AttributeSet attrs)
{
super(context, attrs);
// TODO Auto-generated constructor stub
SurfaceHolder holder = getHolder();
holder.addCallback(this);
setFocusable(true); // make sure we get key events
setFocusableInTouchMode(true); // make sure we get touch events
init();
holder.getSurface();
}
And i could draw on the canvas!!
I am trying to make Custom RelativeLayout which can scale and scroll. Right now I have tried to achieve scale. Now I have made the custom Relative layout as parent layout of another relative layout which contains touchable Imageview as its child. Now when I zoom the parent custom relative layout child also gets zoom but the clickable area of the Imageview translates I dont know why ? When Imageview or layout are at normal position the clickable area is on the Imageview but as soon as the layout gets zoom the clickable area shifts ? I do not know why am I facing weird position displacement of clickable
here is the code
my custom relative layourt
public class scaleLayout extends RelativeLayout {
private float mScaleFactor=1.0f;
private long lastTouchTime = -1;
public scaleLayout(Context context)
{
super(context);
// setWillNotDraw(false);
}
public scaleLayout(Context context, AttributeSet attrs) {
super(context, attrs);
//setWillNotDraw(false);
// TODO Auto-generated constructor stub
}
/* #Override
public boolean dispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
} */
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
return super.onTouchEvent(event);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
long thisTime = System.currentTimeMillis();
if (thisTime - lastTouchTime < 250) {
// Double tap
mScaleFactor=1.5f;
invalidate();
lastTouchTime = -1;
} else {
// Too slow :)
/* mScaleFactor=1.0f;
invalidate();*/
lastTouchTime = thisTime;
}
}
return super.onInterceptTouchEvent(ev);
}
#Override
protected void dispatchDraw(Canvas canvas) {
// TODO Auto-generated method stub
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(mScaleFactor, mScaleFactor);
super.dispatchDraw(canvas);
canvas.restore();
}
#Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
// TODO Auto-generated method stub
return super.invalidateChildInParent(location, dirty);
}
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
int count = getChildCount();
for(int i=0;i<count;i++){
View child = getChildAt(i);
if(child.getVisibility()!=GONE){
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)child.getLayoutParams();
child.layout(
(int)(params.leftMargin * mScaleFactor),
(int)(params.topMargin * mScaleFactor),
(int)((params.leftMargin + child.getMeasuredWidth()) * mScaleFactor),
(int)((params.topMargin + child.getMeasuredHeight()) * mScaleFactor)
);
}
}
}
here is the activity
public class LayoutZoomingActivity extends Activity implements OnTouchListener {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageView img1 = (ImageView) findViewById(R.id.imageView1);
img1.setOnTouchListener(this);
ImageView img2 = (ImageView) findViewById(R.id.imageView2);
img2.setOnTouchListener(this);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
ImageView iv= (ImageView) v;
v.setVisibility(View.INVISIBLE);
return false;
}
this is xml
<com.layoutzooming.scaleLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/gm01"
>
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="jhkibnkij"
android:layout_centerInParent="true"
android:textColor="#FFFFFF"
android:textSize="25dp" />
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/textView1"
android:layout_marginLeft="500dp"
android:layout_marginTop="250dp"
android:background="#000"
android:src="#drawable/dih01" />
<ImageView
android:id="#+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/textView1"
android:layout_marginLeft="350dp"
android:layout_marginTop="250dp"
android:background="#000"
android:src="#drawable/dih02" />
</RelativeLayout>
</com.layoutzooming.scaleLayout>
Some how I managed to fix this issue by adding the below code in LayoutZoomingActivity .
public class LayoutZoomingActivity extends Activity implements OnTouchListener {
ImageView img1; ImageView img2; /** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
img1 = (ImageView) findViewById(R.id.imageView1);
img1.setOnTouchListener(this);
img2 = (ImageView) findViewById(R.id.imageView2);
img2.setOnTouchListener(this);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
Log.d("b","b");
if(isViewContains(img1, (int)event.getX(), (int)event.getY())) {
img1.setVisibility(View.GONE); }
if(isViewContains(img2, (int)event.getX(), (int)event.getY())) {
img2.setVisibility(View.GONE); }
return false;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
Log.d("onTouchEvent c","c"+(int)event.getX()+","+(int)event.getY());
if(isViewContains(img1, (int)event.getX(), (int)event.getY())) {
img1.setVisibility(View.GONE);
img1.invalidate(); }
if(isViewContains(img2, (int)(event.getX()/scaleLayout.mScaleFactor), (int)(event.getY()/scaleLayout.mScaleFactor))) {
img2.setVisibility(View.GONE);
img2.invalidate(); }
return super.onTouchEvent(event);
}
private boolean isViewContains(View view, int rx, int ry) {
int[] l = new int[2];
view.getLocationOnScreen(l);
int x = l[0];
int y = l[1];
int w = view.getWidth();
int h = view.getHeight();
Log.d("isViewContains::"+x+"::"+y+"::"+w+"::"+h,rx+"::"+ry);
if (rx < x || rx > x + w || ry < y || ry > y + h) {
return false;
}
return true;
} }
Can you please try this and customize according to your requirement.
The actual are of the parent layout(Scalable relative layout) is increased so this relative layout and image view both try to adjust themselves to the same position as it was before increasing .Before scaling they had some position relative to the screen and now after scalling they try to adjust to the same position .Try using Linear layout for scalling