안 드 로 이 드 고급 이미지 스크롤 컨트롤 3D 버 전 이미지 재생 기 구현
사진 윤방송 기 에 대해 말하자면 많은 안 드 로 이 드 응용 프로그램 에서 이 기능 을 가지 고 있다.예 를 들 어 왕 이 뉴스,타 오 바 오 등 이다.최근 에 우리 회사 의 응용 프로그램 도 이 기능 을 추 가 했 고 사진 윤 방 을 바탕 으로 3 차원 입체 효 과 를 추 가 했 지만 전체적인 효과 가 이상 적 이지 않 고 사용자 의 체험 성 이 비교적 나쁘다 는 것 이 유감스럽다.그래서 저 는 효과 가 더 좋 은 3D 사진 재생 기 를 만 드 는 데 시간 이 좀 걸 렸 습 니 다.저 는 만족 합 니 다.여기 서 과감하게 블 로 그 를 써 서 여러분 께 공유 하 겠 습 니 다.
먼저 실현 원 리 를 소개 합 니 다.전통 적 인 이미지 재생 기 는 한 화면 에 한 장의 그림 만 표시 되 고 손가락 으로 좌우 로 미 끄 러 져 야 다른 그림 을 볼 수 있 습 니 다.여기 서 우 리 는 사 고 를 발산 하여 한 화면 에 세 장의 그림 을 동시에 표시 한 다음 에 Camera 의 방식 으로 좌우 의 두 장의 그림 을 3D 로 회전 시 키 면 입체 적 인 그림 윤 방송 기 를 만 들 수 있다.원리 설명도 는 다음 과 같다.
사진 을 입체 적 으로 조작 하려 면 Camera 기술 을 사용 해 야 합 니 다.만약 에 이 기술 에 대해 잘 모 르 면 인터넷 에서 관련 자 료 를 검색 하거나 제 앞의 글 을 참고 하 십시오Android 중축 회전 효과 구현 Android 다른 이미지 브 라 우 저 만 들 기
그럼 지금부터 시작 합 시다.먼저 안 드 로 이 드 프로젝트 를 새로 만 들 고 이미지 스위치 뷰 테스트 라 고 이름 을 지 었 습 니 다.
그리고 Image3DView 를 새로 만 들 면 ImageView 의 모든 속성 을 계승 하고 3D 회전 기능 을 추가 합 니 다.코드 는 다음 과 같 습 니 다.
public class Image3DView extends ImageView {
/**
*
*/
private static final float BASE_DEGREE = 50f;
/**
*
*/
private static final float BASE_DEEP = 150f;
private Camera mCamera;
private Matrix mMaxtrix;
private Bitmap mBitmap;
/**
*
*/
private int mIndex;
/**
* X
*/
private int mScrollX;
/**
* Image3DSwitchView
*/
private int mLayoutWidth;
/**
*
*/
private int mWidth;
/**
*
*/
private float mRotateDegree;
/**
*
*/
private float mDx;
/**
*
*/
private float mDeep;
public Image3DView(Context context, AttributeSet attrs) {
super(context, attrs);
mCamera = new Camera();
mMaxtrix = new Matrix();
}
/**
* Image3DView , , 。
*/
public void initImageViewBitmap() {
if (mBitmap == null) {
setDrawingCacheEnabled(true);
buildDrawingCache();
mBitmap = getDrawingCache();
}
mLayoutWidth = Image3DSwitchView.mWidth;
mWidth = getWidth() + Image3DSwitchView.IMAGE_PADDING * 2;
}
/**
* 。
*
* @param index
*
* @param scrollX
* X
*/
public void setRotateData(int index, int scrollX) {
mIndex = index;
mScrollX = scrollX;
}
/**
* Bitmap , 。
*/
public void recycleBitmap() {
if (mBitmap != null && !mBitmap.isRecycled()) {
mBitmap.recycle();
}
}
@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
mBitmap = null;
initImageViewBitmap();
}
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
mBitmap = null;
initImageViewBitmap();
}
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
mBitmap = null;
initImageViewBitmap();
}
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
mBitmap = null;
initImageViewBitmap();
}
@Override
protected void onDraw(Canvas canvas) {
if (mBitmap == null) {
// Bitmap , onDraw
super.onDraw(canvas);
} else {
if (isImageVisible()) {
// , ,
computeRotateData();
mCamera.save();
mCamera.translate(0.0f, 0.0f, mDeep);
mCamera.rotateY(mRotateDegree);
mCamera.getMatrix(mMaxtrix);
mCamera.restore();
mMaxtrix.preTranslate(-mDx, -getHeight() / 2);
mMaxtrix.postTranslate(mDx, getHeight() / 2);
canvas.drawBitmap(mBitmap, mMaxtrix, null);
}
}
}
/**
* 。
*/
private void computeRotateData() {
float degreePerPix = BASE_DEGREE / mWidth;
float deepPerPix = BASE_DEEP / ((mLayoutWidth - mWidth) / 2);
switch (mIndex) {
case 0:
mDx = mWidth;
mRotateDegree = 360f - (2 * mWidth + mScrollX) * degreePerPix;
if (mScrollX < -mWidth) {
mDeep = 0;
} else {
mDeep = (mWidth + mScrollX) * deepPerPix;
}
break;
case 1:
if (mScrollX > 0) {
mDx = mWidth;
mRotateDegree = (360f - BASE_DEGREE) - mScrollX * degreePerPix;
mDeep = mScrollX * deepPerPix;
} else {
if (mScrollX < -mWidth) {
mDx = -Image3DSwitchView.IMAGE_PADDING * 2;
mRotateDegree = (-mScrollX - mWidth) * degreePerPix;
} else {
mDx = mWidth;
mRotateDegree = 360f - (mWidth + mScrollX) * degreePerPix;
}
mDeep = 0;
}
break;
case 2:
if (mScrollX > 0) {
mDx = mWidth;
mRotateDegree = 360f - mScrollX * degreePerPix;
mDeep = 0;
if (mScrollX > mWidth) {
mDeep = (mScrollX - mWidth) * deepPerPix;
}
} else {
mDx = -Image3DSwitchView.IMAGE_PADDING * 2;
mRotateDegree = -mScrollX * degreePerPix;
mDeep = 0;
if (mScrollX < -mWidth) {
mDeep = -(mWidth + mScrollX) * deepPerPix;
}
}
break;
case 3:
if (mScrollX < 0) {
mDx = -Image3DSwitchView.IMAGE_PADDING * 2;
mRotateDegree = BASE_DEGREE - mScrollX * degreePerPix;
mDeep = -mScrollX * deepPerPix;
} else {
if (mScrollX > mWidth) {
mDx = mWidth;
mRotateDegree = 360f - (mScrollX - mWidth) * degreePerPix;
} else {
mDx = -Image3DSwitchView.IMAGE_PADDING * 2;
mRotateDegree = BASE_DEGREE - mScrollX * degreePerPix;
}
mDeep = 0;
}
break;
case 4:
mDx = -Image3DSwitchView.IMAGE_PADDING * 2;
mRotateDegree = (2 * mWidth - mScrollX) * degreePerPix;
if (mScrollX > mWidth) {
mDeep = 0;
} else {
mDeep = (mWidth - mScrollX) * deepPerPix;
}
break;
}
}
/**
* 。
*
* @return true, false。
*/
private boolean isImageVisible() {
boolean isVisible = false;
switch (mIndex) {
case 0:
if (mScrollX < (mLayoutWidth - mWidth) / 2 - mWidth) {
isVisible = true;
} else {
isVisible = false;
}
break;
case 1:
if (mScrollX > (mLayoutWidth - mWidth) / 2) {
isVisible = false;
} else {
isVisible = true;
}
break;
case 2:
if (mScrollX > mLayoutWidth / 2 + mWidth / 2
|| mScrollX < -mLayoutWidth / 2 - mWidth / 2) {
isVisible = false;
} else {
isVisible = true;
}
break;
case 3:
if (mScrollX < -(mLayoutWidth - mWidth) / 2) {
isVisible = false;
} else {
isVisible = true;
}
break;
case 4:
if (mScrollX > mWidth - (mLayoutWidth - mWidth) / 2) {
isVisible = true;
} else {
isVisible = false;
}
break;
}
return isVisible;
}
}
이 코드 는 비교적 길 고 복잡 하 니 천천히 분석 합 시다.Image3DView 의 구조 함수 에서 Camera 와 Matrix 대상 을 초기 화하 여 뒤에서 그림 을 3D 로 조작 합 니 다.그리고 initImageViewBitmap()방법 에서 필요 한 정 보 를 초기 화 했 습 니 다.예 를 들 어 현재 그림 을 캡 처 하여 후속 적 인 입체 작업 에 사용 하고 현재 그림 의 폭 을 얻 는 등 입 니 다.그 다음 에 setRotateData()방법 도 제공 했다.현재 그림 의 아래 표 와 스크롤 거 리 를 설정 하 는 데 사용 된다.이 두 가지 데이터 가 있 으 면 coptute RotateData()방법 으로 회전 각도 의 일부 데 이 터 를 계산 하고 isImageVisible()방법 으로 현재 그림 이 보 이 는 지 여 부 를 판단 할 수 있다.구체 적 이 고 상세 한 알고리즘 논 리 는 코드 를 읽 고 천천히 분석 할 수 있다.
다음 에 그림 을 화면 에 그 려 야 할 때 onDraw()방법 을 사용 하고 onDraw()방법 에서 판단 합 니 다.현재 그림 을 볼 수 있 으 면 coptute RotateData()방법 으로 회전 할 때 필요 한 각종 데 이 터 를 계산 한 다음 에 Camera 와 Matrix 를 통 해 회전 작업 을 수행 하면 됩 니 다.
이 어 ViewGroup 에서 Image3D SwitchView 를 새로 만 들 었 습 니 다.코드 는 다음 과 같 습 니 다.
public class Image3DSwitchView extends ViewGroup {
/**
*
*/
public static final int IMAGE_PADDING = 10;
private static final int TOUCH_STATE_REST = 0;
private static final int TOUCH_STATE_SCROLLING = 1;
/**
*
*/
private static final int SNAP_VELOCITY = 600;
/**
*
*/
private static final int SCROLL_NEXT = 0;
/**
*
*/
private static final int SCROLL_PREVIOUS = 1;
/**
*
*/
private static final int SCROLL_BACK = 2;
private static Handler handler = new Handler();
/**
*
*/
public static int mWidth;
private VelocityTracker mVelocityTracker;
private Scroller mScroller;
/**
* ,
*/
private OnImageSwitchListener mListener;
/**
*
*/
private int mTouchState = TOUCH_STATE_REST;
/**
*
*/
private int mTouchSlop;
/**
*
*/
private int mHeight;
/**
*
*/
private int mImageWidth;
/**
*
*/
private int mCount;
/**
*
*/
private int mCurrentImage;
/**
*
*/
private float mLastMotionX;
/**
*
*/
private boolean forceToRelayout;
private int[] mItems;
public Image3DSwitchView(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
mScroller = new Scroller(context);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed || forceToRelayout) {
mCount = getChildCount();
// 5,
if (mCount < 5) {
return;
}
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
//
mImageWidth = (int) (mWidth * 0.6);
if (mCurrentImage >= 0 && mCurrentImage < mCount) {
mScroller.abortAnimation();
setScrollX(0);
int left = -mImageWidth * 2 + (mWidth - mImageWidth) / 2;
//
int[] items = { getIndexForItem(1), getIndexForItem(2),
getIndexForItem(3), getIndexForItem(4),
getIndexForItem(5) };
mItems = items;
//
for (int i = 0; i < items.length; i++) {
Image3DView childView = (Image3DView) getChildAt(items[i]);
childView.layout(left + IMAGE_PADDING, 0, left
+ mImageWidth - IMAGE_PADDING, mHeight);
childView.initImageViewBitmap();
left = left + mImageWidth;
}
refreshImageShowing();
}
forceToRelayout = false;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mScroller.isFinished()) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
int action = event.getAction();
float x = event.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
//
mLastMotionX = x;
break;
case MotionEvent.ACTION_MOVE:
int disX = (int) (mLastMotionX - x);
mLastMotionX = x;
scrollBy(disX, 0);
//
refreshImageShowing();
break;
case MotionEvent.ACTION_UP:
mVelocityTracker.computeCurrentVelocity(1000);
int velocityX = (int) mVelocityTracker.getXVelocity();
if (shouldScrollToNext(velocityX)) {
//
scrollToNext();
} else if (shouldScrollToPrevious(velocityX)) {
//
scrollToPrevious();
} else {
//
scrollBack();
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
break;
}
}
return true;
}
/**
* 。
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE)
&& (mTouchState != TOUCH_STATE_REST)) {
return true;
}
float x = ev.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
mLastMotionX = x;
mTouchState = TOUCH_STATE_REST;
break;
case MotionEvent.ACTION_MOVE:
int xDiff = (int) Math.abs(mLastMotionX - x);
if (xDiff > mTouchSlop) {
mTouchState = TOUCH_STATE_SCROLLING;
}
break;
case MotionEvent.ACTION_UP:
default:
mTouchState = TOUCH_STATE_REST;
break;
}
return mTouchState != TOUCH_STATE_REST;
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
refreshImageShowing();
postInvalidate();
}
}
/**
* , 。
*
* @param listener
*
*/
public void setOnImageSwitchListener(OnImageSwitchListener listener) {
mListener = listener;
}
/**
* , , 。
*
* @param currentImage
*
*/
public void setCurrentImage(int currentImage) {
mCurrentImage = currentImage;
requestLayout();
}
/**
* 。
*/
public void scrollToNext() {
if (mScroller.isFinished()) {
int disX = mImageWidth - getScrollX();
checkImageSwitchBorder(SCROLL_NEXT);
if (mListener != null) {
mListener.onImageSwitch(mCurrentImage);
}
beginScroll(getScrollX(), 0, disX, 0, SCROLL_NEXT);
}
}
/**
* 。
*/
public void scrollToPrevious() {
if (mScroller.isFinished()) {
int disX = -mImageWidth - getScrollX();
checkImageSwitchBorder(SCROLL_PREVIOUS);
if (mListener != null) {
mListener.onImageSwitch(mCurrentImage);
}
beginScroll(getScrollX(), 0, disX, 0, SCROLL_PREVIOUS);
}
}
/**
* 。
*/
public void scrollBack() {
if (mScroller.isFinished()) {
beginScroll(getScrollX(), 0, -getScrollX(), 0, SCROLL_BACK);
}
}
/**
* , 。
*/
public void clear() {
for (int i = 0; i < mCount; i++) {
Image3DView childView = (Image3DView) getChildAt(i);
childView.recycleBitmap();
}
}
/**
* 。
*/
private void beginScroll(int startX, int startY, int dx, int dy,
final int action) {
int duration = (int) (700f / mImageWidth * Math.abs(dx));
mScroller.startScroll(startX, startY, dx, dy, duration);
invalidate();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (action == SCROLL_NEXT || action == SCROLL_PREVIOUS) {
forceToRelayout = true;
requestLayout();
}
}
}, duration);
}
/**
* item , item 。
*
* @param item
* 1-5
* @return item 。
*/
private int getIndexForItem(int item) {
int index = -1;
index = mCurrentImage + item - 3;
while (index < 0) {
index = index + mCount;
}
while (index > mCount - 1) {
index = index - mCount;
}
return index;
}
/**
* , 。
*/
private void refreshImageShowing() {
for (int i = 0; i < mItems.length; i++) {
Image3DView childView = (Image3DView) getChildAt(mItems[i]);
childView.setRotateData(i, getScrollX());
childView.invalidate();
}
}
/**
* , 。
*/
private void checkImageSwitchBorder(int action) {
if (action == SCROLL_NEXT && ++mCurrentImage >= mCount) {
mCurrentImage = 0;
} else if (action == SCROLL_PREVIOUS && --mCurrentImage < 0) {
mCurrentImage = mCount - 1;
}
}
/**
* 。
*/
private boolean shouldScrollToNext(int velocityX) {
return velocityX < -SNAP_VELOCITY || getScrollX() > mImageWidth / 2;
}
/**
* 。
*/
private boolean shouldScrollToPrevious(int velocityX) {
return velocityX > SNAP_VELOCITY || getScrollX() < -mImageWidth / 2;
}
/**
*
*/
public interface OnImageSwitchListener {
/**
*
*
* @param currentImage
*
*/
void onImageSwitch(int currentImage);
}
}
이 코드 도 비교적 길 기 때문에 우 리 는 조금씩 분석 을 진행 합 시다.onLayout()방법 에 서 는 먼저 하위 보기 의 개수 가 5 보다 큰 지 판단 해 야 합 니 다.5 개가 부족 하면 그림 재생 기 가 정상적으로 표시 되 지 않 고 return 으로 떨 어 집 니 다.5 개 이상 이면 하나의 for 순환 을 통 해 모든 하위 보기 에 표 시 된 위 치 를 할당 합 니 다.각 하위 보 기 는 하나의 Image3DView 이 고,for 순환 에 서 는 Image3DView 의 initImageViewBitmap()방법 을 사용 하여 모든 컨트롤 에 초기 화 작업 을 수행 한 후 refreshImageShowing()방법 으로 그림 의 표시 상 태 를 새로 고 칩 니 다.이 어 손가락 이 Image3D SwitchView 컨트롤 에서 미 끄 러 질 때 onTouchEvent()방법 에 들 어 갑 니 다.손가락 을 누 를 때 누 를 때의 가로 좌 표를 기록 한 다음 손가락 이 미 끄 러 질 때 미 끄 러 지 는 거 리 를 계산 하고 scrollBy()방법 으로 스크롤 합 니 다.손가락 이 화면 을 떠 날 때 현재 미 끄 러 지 는 거리 와 속 도 를 결정 합 니 다.다음 그림 으로 스크롤 하 시 겠 습 니까?이전 그림 으로 스크롤 하 시 겠 습 니까?원본 그림 으로 스크롤 하 시 겠 습 니까?각각 호출 하 는 방법 은 scrollToNext(),scrollToPrevious(),scrollBack()입 니 다.
scrollToNext()방법 에 서 는 스크롤 해 야 할 거 리 를 계산 한 다음 경계 검 사 를 통 해 현재 그림 의 아래 표 시 를 합 리 적 인 범 위 를 초과 하지 않도록 한 다음 begin Scroll()방법 으로 스크롤 합 니 다.begin Scroll()방법 에 서 는 스크롤 러 의 startScroll()방법 을 호출 하여 스크롤 작업 을 수행 하 는 것 입 니 다.스크롤 이 끝 난 후에 requestLayout()방법 으로 다시 레이아웃 을 요구 합 니 다.그 후에 onLayout()방법 은 다시 실 행 됩 니 다.그림 마다 위치 가 바 뀝 니 다.scrollToPrevious()와 scrollBack()방법의 원리 도 마찬가지 로 중복 분석 하지 않 습 니 다.
그렇다면 onLayout()방법의 마지막 호출 된 refreshImageShowing()방법 은 도대체 어떤 조작 을 실 행 했 습 니까?사실은 모든 Image3DView 컨트롤 을 옮 겨 다 니 며 setRotateData()방법 을 사용 하고 그림 의 아래 표지 와 스크롤 거 리 를 전달 하면 모든 그림 이 어떻게 회전 해 야 하 는 지 알 수 있 습 니 다.
다른 일부 다른 세부 사항 은 여기 서 설명 하지 않 습 니 다.주석 은 비교적 상세 하 게 쓰 여 있 습 니 다.당신 은 천천히 분석 하고 이해 할 수 있 습 니 다.
그러면 Image3D SwitchView 를 어떻게 사용 하 는 지 알 아 보 겠 습 니 다.이 컨트롤 을 열거 나 새로 만 드 는 activitymain.xml 는 프로그램의 주 레이아웃 파일 로 코드 는 다음 과 같 습 니 다.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff" >
<com.example.imageswitchviewtest.Image3DSwitchView
android:id="@+id/image_switch_view"
android:layout_width="match_parent"
android:layout_height="150dp" >
<com.example.imageswitchviewtest.Image3DView
android:id="@+id/image1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/image1" />
<com.example.imageswitchviewtest.Image3DView
android:id="@+id/image2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/image2" />
<com.example.imageswitchviewtest.Image3DView
android:id="@+id/image3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/image3" />
<com.example.imageswitchviewtest.Image3DView
android:id="@+id/image4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/image4" />
<com.example.imageswitchviewtest.Image3DView
android:id="@+id/image5"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/image5" />
<com.example.imageswitchviewtest.Image3DView
android:id="@+id/image6"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/image6" />
<com.example.imageswitchviewtest.Image3DView
android:id="@+id/image7"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/image7" />
</com.example.imageswitchviewtest.Image3DSwitchView>
</RelativeLayout>
여기에서 우 리 는 Image3DSwitchView 컨트롤 을 도입 한 다음 에 이 컨트롤 아래 에 7 개의 Image3DView 컨트롤 을 추가 했다.모든 Image3DView 는 사실상 ImageView 이기 때문에 우 리 는 android:src 를 통 해 그림 을 지정 할 수 있다.앞에서 도 말 했 듯 이 Image3D SwitchView 컨트롤 의 하위 컨트롤 은 5 개 이상 이 어야 합 니 다.그렇지 않 으 면 정상적으로 표시 되 지 않 습 니 다.코드 는 여기까지 쓰 면 별로 차이 가 나 지 않 습 니 다.지금 프로그램 을 실행 하면 3D 버 전의 그림 재생 기 를 볼 수 있 습 니 다.손가락 으로 미 끄 러 지면 더 많은 그림 을 볼 수 있 습 니 다.다음 그림 과 같 습 니 다.
어때요?그래도 효과 가 좋 죠!이외에 도 Image3DSwitchView 에 서 는 setCurrentImage()방법 과 setOnImageSwitchListener()방법 을 제공 하여 현재 어떤 그림 을 표시 할 지 설정 하고 그림 이 굴 러 가 는 감청 기 를 설정 할 수 있 습 니 다.이러한 방법 이 있 으 면 Image3DSwitchView 를 바탕 으로 더욱 쉽게 확장 할 수 있 습 니 다.예 를 들 어 페이지 서명 표시 기능 을 추가 하 는 등 입 니 다.
자,오늘 설명 은 여기까지 입 니 다.의문 이 있 는 친 구 는 아래 에 메 시 지 를 남 길 수 있 습 니 다.
원본 다운로드,여 기 를 클릭 하 세 요.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.