Как создать 3D-прокрутку для текста? (Как видно в Appy Geek)

Я ищу, чтобы реализовать какой-то «холст», где вы можете поместить X число TextViews / Links в «случайные позиции» (позиционируется как на изображении ниже). Затем вы сможете прокручивать этот «холст» влево или вправо непрерывно, и представление будет повторяться / быть круговым (вроде как выделение HTML, за исключением того, что вы выполняете прокрутку вручную). В самом простом случае я просто хочу иметь горизонтальную прокрутку, но пример более сложного случая – это то, где вы можете «скроллировать сферу» – см. Пример ниже от Appy Geek. (На данный момент меня просто интересует горизонтальная прокрутка)

Пример из Appy Geek:

Введите описание изображения здесь

Solutions Collecting From Web of "Как создать 3D-прокрутку для текста? (Как видно в Appy Geek)"

Ну, это поможет вам начать, я реализовал простое облако тегов, используя оба подхода (т. ViewGroup Расширяя View и ViewGroup ), который продолжает вращаться. Вы можете использовать эту логику в своей пользовательской ViewGroup которая позиционирует свой вид. После этого добавьте интерактивный TextView внутри этого макета и обработайте события касания.

Конечный результат (конечно, его вращение, взгляд ближе):

Введите описание изображения здесь

В следующем коде можно улучшить множество вещей.

ПО РАСШИРЕНИЮ ViewGroup :

Поместите это в xml-макет:

  <com.vj.tagcloud.TagCloudLayout android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" /> </com.vj.tagcloud.TagCloudLayout> 

Класс TagCloudLayout :

 import java.util.Random; import android.content.Context; import android.os.Handler; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class TagCloudLayout extends ViewGroup { final Random mRandom = new Random(); private float mRotateAngle; private Handler mHandler = new Handler(); private float rotateAngleDegree; public TagCloudLayout(Context context) { super(context); } public TagCloudLayout(Context context, AttributeSet attrs) { super(context, attrs); } public TagCloudLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); final float radius = Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2F; float halfWidth = getMeasuredWidth() / 2F; float halfHeight = getMeasuredHeight() / 2F; final int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); LayoutParams lp = (LayoutParams) child.getLayoutParams(); float sinTheta = (float) Math.sin(lp.theta); float x = (int) (radius * Math.cos(lp.fi + mRotateAngle) * sinTheta); if (child instanceof TextView) { ((TextView) child) .setTextSize(15 * ((radius - x) / radius) + 10); } measureChild(child, widthMeasureSpec, heightMeasureSpec); // http://en.wikipedia.org/wiki/Spherical_coordinates lp.x = (int) ((halfWidth + radius * Math.sin(lp.fi + mRotateAngle) * sinTheta) - /* for balancing on x-axis */(child .getMeasuredWidth() / 2F)); lp.y = (int) (halfHeight + radius * Math.cos(lp.theta)-/* for balancing on y-axis */(child .getMeasuredHeight() / 2F)); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); mHandler.postDelayed(new Runnable() { @Override public void run() { rotateAngleDegree += 5; mRotateAngle = (float) Math.toRadians(rotateAngleDegree); requestLayout(); mHandler.postDelayed(this, 40); } }, 40); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mHandler.removeCallbacksAndMessages(null); } @Override public void addView(View child, int index, android.view.ViewGroup.LayoutParams params) { super.addView(child, index, params); LayoutParams lp = (LayoutParams) child.getLayoutParams(); lp.fi = (float) Math.toRadians(mRandom.nextInt(360)); lp.theta = (float) Math.toRadians(mRandom.nextInt(360)); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); LayoutParams lp = (LayoutParams) child.getLayoutParams(); child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight()); } } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof LayoutParams; } @Override protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); } @Override protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return new LayoutParams(p.width, p.height); } public static class LayoutParams extends ViewGroup.LayoutParams { int x; int y; float fi, theta; public LayoutParams(Context context, AttributeSet attrs) { super(context, attrs); } public LayoutParams(int w, int h) { super(w, h); } } } 

ПО РАСШИРЕНИЮ View :

Поместите это в xml-макет:

 <com.vj.wordtap.TagCloud android:layout_width="match_parent" android:layout_height="match_parent" /> 

И это в java-коде:

 import java.util.ArrayList; import java.util.List; import java.util.Random; import android.content.Context; import android.graphics.Camera; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.Handler; import android.text.TextPaint; import android.util.AttributeSet; import android.view.View; public class TagCloud extends View { private List<String> mItems = new ArrayList<String>(); private List<Angles> mAngles = new ArrayList<Angles>(); private Camera mCamera = new Camera(); private TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); private Handler mHandler = new Handler(); private float mRotateAngle; private float rotateAngleDegree; public static class Angles { float fi, theta; } public TagCloud(Context context) { super(context); init(); } public TagCloud(Context context, AttributeSet attrs) { super(context, attrs); init(); } public TagCloud(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { List<String> items = new ArrayList<String>(); for (int i = 0; i < 10; i++) { items.add("item:" + i); } setItems(items); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.translate(canvas.getWidth() / 2F, canvas.getHeight() / 2F); mTextPaint.setColor(Color.BLACK); final float radius = 100; mCamera.setLocation(0, 0, -100); for (int i = 0; i < mItems.size(); i++) { String item = mItems.get(i); Angles xyz = mAngles.get(i); mCamera.save(); canvas.save(); float sinTheta = (float) Math.sin(xyz.theta); float x = (float) (radius * Math.cos(xyz.fi + mRotateAngle) * sinTheta); float y = (float) (radius * Math.sin(xyz.fi + mRotateAngle) * sinTheta); float z = (float) (radius * Math.cos(xyz.theta)); // mapping coordinates with Android's coordinates // http://en.wikipedia.org/wiki/Spherical_coordinates mCamera.translate(y, z, x); mCamera.applyToCanvas(canvas); // http://en.wikipedia.org/wiki/Spherical_coordinates // set size based on x-Axis that is coming towards us mTextPaint.setTextSize(20 * ((100 - x) / 100) + 10); canvas.drawText(item, 0, 0, mTextPaint); mCamera.restore(); canvas.restore(); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); mHandler.postDelayed(new Runnable() { @Override public void run() { rotateAngleDegree += 5; mRotateAngle = (float) Math.toRadians(rotateAngleDegree); invalidate(); mHandler.postDelayed(this, 40); } }, 40); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mHandler.removeCallbacksAndMessages(null); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } public void setItems(List<String> items) { mItems = items; final Random ran = new Random(); final List<Angles> xyzList = mAngles; xyzList.clear(); for (int i = 0; i < items.size(); i++) { Angles xyz = new Angles(); float fi = (float) Math.toRadians(ran.nextInt(360)); xyz.fi = fi; float theta = (float) Math.toRadians(ran.nextInt(360)); xyz.theta = theta; xyzList.add(xyz); } } }