안드로이드 웹 통신하기


안드로이드 진저브레드 이상에서는 Main Thread에서 네트워킹 관련 클래스 사용하면 에러가 발생합니다.

그래서 네트워킹 관련 클래스를 사용할때는 다른 Thread를 사용해야 합니다.



아래 예제는 String인 urlStr에 입력되어있는 웹페이지의 내용을 가져와서 TextView에 보여주는 예제입니다.

HttpUrlConnection은 네트워킹 관련 클래스 이므로 별도의 Thread를 생성해서 사용해야합니다.

HttpURlConnection을 사용 할 때 몇가지 속성을 더 추가해 줄 수 있습니다.

  • .setConnectTime(10000) - 해당 웹 페이지 연결시간을 10초로 설정
  • .setRequestMethod("POST") - Get/Post 방식 설정 
  • .setDoOutput(true) - outputStream으로 데이터를 넘겨주겠다고 설정
  • .setDoInput(true) - InputStream으로 데이터를 읽겠다고 설정  
  • .getResponseCode() - 연결 상태 확인
아래 코드에서 getResponseCode()를 통해 웹 페이지와 연결이 됐는지 확인하고 됐으면 웹 페이지의 글들을 가져와서 textView에 보여줍니다.
연결되지 않았다면 에러 발생이라는 토스트 메세지를 보여줍니다.
class mThread extends Thread{
@Override
public void run() {
try {
URL url = new URL(urlStr);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
if(urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK){
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
readStream(in);
urlConnection.disconnect();
}else{
Toast.makeText(getApplicationContext(), "에러발생", Toast.LENGTH_SHORT).show();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

}
}

public void readStream(InputStream in){
final String data = readData(in);
mHandler.post(new Runnable() {
@Override
public void run() {
tv.setText(data);
}
});
}
public String readData(InputStream is){
String data = "";
Scanner s = new Scanner(is);
while(s.hasNext()) data += s.nextLine() + "\n";
s.close();
return data;
}

Handler mHandler = new Handler();


아래 예제는 HttpURLConnection을 사용했습니다.

 


  


 


https://github.com/Ywook/AndroidPractice12


안드로이드 handler, AsyncThread 사용하기


Handler


안드로이드의 UI는 메인 스레드에서만 변경할 수 있습니다.

그래서 만약 서브 스레드에서 UI를 변경하고 싶으면 Handler를 사용해야합니다.

즉, 서브 스레드 -> Handler -> 메인 스레드 과정을 거쳐서 서브 스레드에서 UI를 변경할 수 있습니다.


아래 예제는 서브 스레드에서 TextView의 글자를 바꾸는 예제입니다.

Handler를 생성하는 스레드만 다른 스레드가 보내는 Message 객체나 Runnable 객체를 받을 수 있으므로 

메인 스레드에서 Handler를 생성해 줘야합니다.

public class MainActivity extends AppCompatActivity {
TextView tv;
int i;

Handler mhandler = new Handler(){
@Override
public void handleMessage(Message msg) {
tv.setText("숫자 : "+ i);

}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

tv = (TextView)findViewById(R.id.text1);
}

public void onClick(View v){
SubThread th = new SubThread();
th.start();
}

class SubThread extends Thread{
@Override
public void run() {
for(i = 1; i <= 10; i++){
try {
Thread.sleep(1000);
Message msg = mhandler.obtainMessage();
mhandler.sendMessage(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}


Handler를 이용해서 로딩화면을 구현해 줄 수 있습니다.

Handler의 postDelayed는 첫 번째 인자로 Runnable 객체, 두 번째 인자로 입력된 시간 후에 Runnable객체가 실행되게 합니다.

아래 예제에서는 3초후에 화면이 바뀌도록 설정한 예제입니다.

public class LoadingActivity extends AppCompatActivity {
Handler mhandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loading);
mhandler = new Handler();
mhandler.postDelayed(new Runnable() {
@Override
public void run() {
Intent i = new Intent(LoadingActivity.this, MainActivity.class);
startActivity(i);
finish();
}
}, 3000);
}
}


AsyncTask


AsyncTask는 Handler를 사용하지 않고 UI 처리를 할 수 있고, Background 작업을 할 수  있습니다.

AsyncTask에는 5개의 Override 메소드가 있습니다.

  • onPreExecute() - AsyncTask가 실행될 때 처음 실행되는 메소드로 UI 변경을 할 수 있습니다.
  • doInBackground(Params...) - 백그라운드 스레드에서 처리되는 메소드 입니다. 진행 중 UI작업이 필요한 경우 publish Progress함수를 호출하면 UI 작업을 하는 onProgressUpdate가 호출됩니다.
  • onProgressUpdate - doInBackground에서 publishProgress를 호출하면 실행되는 메소드로 UI를 변경할 수 있습니다.
  • onPostExecute(Result) - doInBackGround 메소드 종료 후 호출되고 doInBackGround의 리턴 값을 받습니다.
  • onCancelled() - doInBackground 메소드에서 작업이 호출되면 onPostExecute 대신 호출됩니다.


생성법은 아래와 같습니다.

AsyncTask<String, Integer, Long>  

  • String - doInBackground 메소드의 전달 값 
  • Integer - onProgressUpdate 메소드의 전달 값
  • Void - doInBackground의 반환 값

class myTask extends AsyncTask<String,Integer,Void>{
@Override
protected void onPreExecute() {
super.onPreExecute();
}

@Override
protected Long doInBackground(String... params) {
return null;
}

@Override
protected void onProgressUpdate(Integer... values) {
}

@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}


@Override
protected void onCancelled() {
super.onCancelled();
}
}

생성한 AsyncTask를 실행하고 싶을 때는 .execute() 종료하고 싶을 때는 .cancel(true)를 생성합니다.

.execute에 넣어준 인자는 doInBackground에서 사용할 수 있습니다.

myTask task = new myTask();
task.execute(0);
task.cancel(true);


아래 예제는 Handler와 AsyncTask를 사용한 예제입니다.

코드는 깃 허브에서 볼 수 있습니다.

https://github.com/Ywook/AndroidPractice11


  


 


안드로이드 Canvas, Paint 사용하기




간단하게 말하면 Canvas는 도화지, Paint는 붓이라고 할 수 있습니다.

Paint에서 선의 굵기, 색상, 모양등을 선택해서 다양한 형태로 그릴 수 있습니다.

  • View를 상속받는 Custom클래스를 생성
  • Canvas에 그리고 싶은 내용을 onDraw에 코딩
Paint에서 사용되는 메소드
  • setColor(int color) - Paint의 색상 설정
  • setStrokeWidth(float width) - Paint의 굵기를 설정
  • setStyle(Paint.Style style) - Paint 스타일을 설정
  • setTextSize(float textSize) - Paint의 글자 크기를 설정
  • setAntiAlias(boolean aa) - Paint의 경계면을 부드럽게 처리할지 설정
등등..

Canvas에서 사용되는 메소드
  • void drawRect(left, top, right, bottom) - (left, top) 와 (right, bottom)를 대각선으로 가지는 사각형을 그림
  • void drawText(text, x, y, paint) - (x,y) 좌표에 문자를 출력
  • void drawLine(startX, startY, stopX, stopY, paint) - (x,y) 좌표부터 (x,y) 좌표에 선을 그림
  • void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) - 왼쪽 모서리 좌표를 (x,y)로 가지는  bitmap 을 그림
등등..

public class MyCanvas extends View {

public MyCanvas(Context context) {
super(context);
}

public MyCanvas(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawRect(10, 10, 100, 100, paint);

}

}


Canvas에 이미지 그리기

Bitmap img = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);
canvas.drawBitmap(img, 300, 350, paint);


만약 이미지를 작게 또는 크게 그리고 싶을때는 createScaledBitmap을 사용합니다.

Bitmap smallimg = Bitmap.createScaledBitmap(img, img.getWidth()/2, img.getHeight()/2, false);
canvas.drawBitmap(smallimg, 400, 350, paint);

Bitmap bigimg = Bitmap.createScaledBitmap(img, img.getWidth()*2, img.getHeight()*2, false);
canvas.drawBitmap(bigimg, 100, 200, paint);


Touch event 추가하기

-Canvas에 touch event를 추가하려면 onTouchEvent메소드를 Override하고 

 return 값이 false일 경우 touch event를 받지 않겠다는 것이므로 return 값을 true로 해줘야합니다

화면에 터치된 x좌표와 y좌표는 .getX(), getY()를 통해 알 수 있습니다.

getX와 getY의 반환 값은 float이므로 int에 값을 저장하고 싶으면 형변환을 해줘야합니다.

@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int)event.getX();
int y = (int)event.getY();
    invalidate();
return true;
}


Mask Filter 추가하기


1) View의 LayerType을 Software로 변경

public MyCanvas(Context context) {
super(context);
this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

2) BlurMaskFilter를 생성해서 Paint 요소에 적용

- 첫번째 인자는 Blur의 정도 두번째 인자는 Blur를 줄 타입입니다.

BlurMaskFilter blur = new BlurMaskFilter(100,
BlurMaskFilter.Blur.INNER);
mPaint.setMaskFilter(blur);



아래 예제는 Canvas, Paint, 더블 버퍼링을 사용한 예제입니다.

코드는 깃허브에서 볼 수 있습니다.

https://github.com/Ywook/AndroidPractice10


      


    



안드로이드 파일 입출력 예제


파일 입출력을 할 때 내장 메모리 파일을 처리하는 경우와 외장 메모리(SD card) 파일을 처리하는 경우가 있습니다.



안드로이드에서 Byte 단위 처리를 할 때는 

  • FileInputStream/FileOutputStream
  • BufferedInputStream/BufferedoutputStream


문자 단위 처리 할 때는

  • FileReader/FileWiter
  • BufferedReader/BufferedWriter

클래스를 사용합니다.


내장 메모리 파일 처리


내장 메모리 파일 쓰기

- 아래 코드에서 FileWriter의 첫 번째 인자인 getFilesDir()는 파일이 저장되는 디렉토리를 나타내는 경로입니다.

  두 번째 인자가 true일 경우 기존 파일이 존재할 경우 이어쓰기를 하고 false일 경우 기존 파일이 존재할 경우 덮어쓰기를 합니다.

try{
BufferedWriter bw = new BufferedWriter(new FileWriter(getFilesDir() + "test.txt", true));
bw.write("안녕하세요 Hello");
bw.close();

Toast.makeText(this,"저장완료", Toast.LENGTH_SHORT).show();
}catch (Exception e){
e.printStackTrace();
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}


내장 메모리 파일 읽기

-  FileReader의 첫 번째 인자로 읽을 파일의 경로를 지정해줍니다.

readLine() 메소드는 줄 단위로 읽기 때문에 while문을 사용해서 끝까지 읽도록 해야합니다. 

try{
BufferedReader br = new BufferedReader(new FileReader(getFilesDir()+"test.txt"));
String readStr = "";
String str = null;
while(((str = br.readLine()) != null)){
readStr += str +"\n";
}
br.close();

Toast.makeText(this, readStr.substring(0, readStr.length()-1), Toast.LENGTH_SHORT).show();

}catch (FileNotFoundException e){
e.printStackTrace();
Toast.makeText(this, "File not Found", Toast.LENGTH_SHORT).show();
}catch (IOException e) {
e.printStackTrace();
}


외장 메모리 파일 처리(SD cared)


외장 메모리에 파일을 쓰기 위해서는 Permission이 필요합니다.
API 23 마시멜로 버전부터는 권한이 일반권한(Normal Permission)위험권한(Dangerous Permission)으로 나뉘게됐습니다.
위험 권한은 Permission 이외에도 별도의 권한을 요청하는 코드를 작성해야합니다.
외장 메모리 파일 쓰기는 위험 권한으로 지정되어 있습니다.

먼저 manifest에서 permission 코드를 작성합니다.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

아래 checkFunction()이 실행되면서 외부 메모리에 파일을 쓸 권한이 있는지 체크합니다.
이때 requestPermissions가 권한을 요청하는 팝업창을 띄우는 역할을 합니다.

onRequestPermissionsResult Override 메소드는 해당 권한의 사용자 수락 여부를 확인하는 메소드입니다.

public void checkFunction(){
int permissioninfo = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if(permissioninfo == PackageManager.PERMISSION_GRANTED){
Toast.makeText(this,"SDCard 쓰기 권한 있음",Toast.LENGTH_SHORT).show();
}else{
if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)){
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},100);

}else{
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},100);
}
}
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
String str = null;
if(requestCode == 100){
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
str = "SD Card 쓰기권한 승인";
else str = "SD Card 쓰기권한 거부";
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
}
}


외장 메모리 경로 불러오기
-외장 메모리의 경로는 기기마다 다르게 인식할 수 있기 때문에  절대 경로로 지정해줘야합니다. 

getExternalPath()는 외장 메모리 경로를 반환해주는 메소드입니다.
Environment.MEDIA_MOUNTED는 외장 메모리가 존재하고 읽고 쓰기가 가능한 상태를 나타냅니다.
만약 저 상태가 아닐경우 내장 메모리의 경로를 반환합니다.

public String getExternalPath(){

    String sdPath ="";
String ext = Environment.getExternalStorageState();
if(ext.equals(Environment.MEDIA_MOUNTED)){
sdPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/";
}else{
sdPath = getFilesDir() +"";

}
return sdPath;
}

외장 메모리 파일 처리

-외장 메모리 파일 처리는 내장 메모리 파일 처리하는 경우와 비슷합니다.


파일읽기

try{
String path = getExternalPath();
BufferedReader br = new BufferedReader(new FileReader(path+title));
String readStr = "";
String str = null;
while(((str = br.readLine()) != null)){
readStr += str +"\n";
}
br.close();


}catch (FileNotFoundException e){
e.printStackTrace();
Toast.makeText(this, "File not Found", Toast.LENGTH_SHORT).show();
}catch (IOException e) {
e.printStackTrace();
}

파일쓰기

try{
String path = getExternalPath();
String filename = title;

BufferedWriter bw = new BufferedWriter(new FileWriter(path + filename, false));
bw.write("Hello");
bw.close();
Toast.makeText(this,"저장완료", Toast.LENGTH_SHORT).show();
}catch (Exception e){
e.printStackTrace();
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}


아래 예제는 파일 입출력을 사용한 예제입니다.


  


  


  



위 예제의 모든 코드는 깃허브에서 볼 수 있습니다.

https://github.com/Ywook/AndroidPractice9


안드로이드 웹 파일 저장 및 통신



안드로이드 스튜디오에 웹 파일을 추가하려면 Android mode에서 app을 오른쪽 버튼 클릭을 하고 아래와 같은 방법으로

assets 폴더를 생성합니다.


그리고 assets 폴더에 추가할 웹 파일을 저장합니다.

웹 파일을 추가할 때는 Android resource file이 아닌 File을 클릭해서 추가해야합니다.



assets 폴더에 추가한 웹 파일을 웹뷰에서 불러오는 방법입니다.

이 예제에서는 assets폴더의 www폴더를 만들고 www폴더 웹 파일을 추가했기 때문에 /www/urladd.html 경로를 사용했습니다.

webView.loadUrl("file:///android_asset/www/urladd.html");


추가한 웹 파일과 앱간의 JavaScript를 이용하여 통신하려면 통신할때 사용할 클래스를 생성해야합니다.

class JavaScriptMethod{
@JavascriptInterface
public void addSite(String url, String title){
for(int i = 0 ; i < data.size(); i++){
if(data.get(i).getUrl().equals(url)){
handler.post(new Runnable() {
@Override
public void run() {
webView.loadUrl("javaScript:displayMsg()");
}
});
return;
}
}
Sitedata temp = new Sitedata(url, title);
data.add(temp);
titles.add(temp.toString());
adapter.notifyDataSetChanged();
}
@JavascriptInterface
public void showUrl(){
handler.post(new Runnable() {
@Override
public void run() {
linear.setVisibility(View.VISIBLE);
}
});
}
}

그리고 webView.addJavascriptInterface(new JavaScriptMethod(), "myApp")를 추가하면 아까 만들었던 JavaScriptMethod 클래스를 웹 파일에서 myApp이라는 이름으로 사용할 수 있게 됩니다.


만약 웹 파일에 있는 JavaScript로 만들어진 function을 액티비티에서 호출하려면 아래와 같은 방법을 사용합니다.

이 때 웹 파일에 displayMsg()라는 function이 선언되어 있어야합니다.

webView.loadUrl("javaScript:displayMsg()");


아래 예제는 웹 파일 저장 및 통신, 애니메이션을 사용한 예제입니다.

    


    


위 예제의 모든 코드는 깃허브에서 볼 수 있습니다.

https://github.com/Ywook/AndroidPractice8


안드로이드 웹뷰(WebView) 사용하기


웹뷰

 - 웹뷰는 앱 내에 웹화면을 띄우는 뷰를 말합니다.


웹뷰는 인터넷을 사용하기 때문에 웹뷰를 사용하기 위해선 인터넷 접속권한을 등록해야합니다.

manifests 파일에 들어가서 아래에 밑줄 쳐진 permission을 추가합니다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.examples.androidpractice8">

<uses-permission android:name="android.permission.INTERNET"/>

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>


웹뷰를 사용할 레이아웃에 들어가서 웹뷰를 추가합니다.

 

 


티비티 파일에서 웹뷰를 선언 및 객체화를 시켜주고 여러가지 설정이 있는데 몇가지 예를 들면


.setJavaScriptEnabled - 웹뷰에서 자바 스크립트 사용

.setSupportZoom - 손으로 확대, 축소를 할 수 있도록 사용

.setBuiltInZoomControls - WebView 내장 Zoom 사용


등이 있습니다.



이 상태에서 loadUrl을 통해 페이지를 불려오려고 하면  외부 브라우저를 실행하게 됩니다.

웹뷰 내에서 웹 페이지를 돌아다니기 위해선 WebViewClient의 shouldOverrideUrlLoading 함수를 사용해야합니다.  


.onPageStarted() - 웹뷰에서 url이 로드 될때 호출 되는 함수

.onPageFinished() - 웹뷰에서 url 로딩이 완료되면 호출 되는 함수 


위에 2가지 메소드를 추가하면 url이 로드될 때, 완료 되었을 때 상황을 설정해 줄 수 있습니다.


아래 코드에서는 로드가 시작되면 ProgressDialog를 실행하고 완료되면 로드가 완료된 url을 editText에 설정해주는 예제입니다.

webView.setWebViewClient(new WebViewClient(){
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
progressDialog.setMessage("Loading");
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.show();
}

@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return super.shouldOverrideUrlLoading(view, request);
}

@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
et.setText(url);

}
});

웹에서 띄우는 팝업 창을 웹뷰를 통해 보여주게 하려면 .setWebChromeClient()를 설정해야 합니다. 


.onProgressChanged() - 위의 코드에서 설정해준 ProgressDialog가 로딩이 완료되면 값이 100이상이 되는데 이 경우

ProgressDialog를 사라자기게 하기 위해 사용합니다.

.onJSAlert() -  웹에서 띄우는 팝업 창 같은 것을 웹뷰에서 보여주기 위해 사용합니다.

webView.setWebChromeClient(new WebChromeClient(){
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
if(newProgress >= 100) progressDialog.dismiss();
}

@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
});


마지막으로 웹뷰에서 url을 로드할때 loadUrl을 사용합니다.

webView.loadUrl("http://www.naver.com");


안드로이드 CustomWidget 만들기


CustomWidget을 만들기 위해서 4단계를 거쳐야 합니다.

  1. CustomWidget의 Layout생성
  2. Widget을 상속받는 Custom클래스 생성
  3. Custom Class에 생성자 추가
  4. Custom Widget에 Custom Layout inflating


1. CustomWidget의 Layout생성

-CustomWidget에 표현하고 싶은 Layout을 만듭니다.


2.Widget을 상속받는 Custom클래스 생성

-아래와 같이 Widget을 상속하는 클래스를 생성합니다.

public class CustomWidget extends LinearLayout {


}

3. Custom Class에 생성자 추가

-여러가지 생성자가 있는데 xml파일에서도 Class에서 만든 CustomWidget을 사용하기 위해서

 AttributeSet이 추가된 생성자를 추가해야합니다.

public class CustomWidget extends LinearLayout {

    public CustomWidget(Context context, @Nullable AttributeSet attrs) {

        super(context, attrs);

    }

}


4.Custom Widget에 Custom Layout inflating

-1번에서 작성했던 Layout을 Custom Widget에 inflating 해줍니다.

public class CustomWidget extends LinearLayout implements View.OnClickListener{
AutoCompleteTextView autoEt;
ImageView img;
Button add_btn;
Button next_btn;

int imageno = 0;

public CustomWidget(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}

private void init(Context context){
LayoutInflater.from(context).inflate(R.layout.customwidget,this);

autoEt = (AutoCompleteTextView)findViewById(R.id.autoCompleteTextView);
img = (ImageView)findViewById(R.id.image1);
add_btn = (Button)findViewById(R.id.b_add);
next_btn = (Button)findViewById(R.id.b_next);

add_btn.setOnClickListener(this);
next_btn.setOnClickListener(this);
    } 


그리고 만든 CustomWidget을 추가해주고 싶은 xml파일에 추가해주면 됩니다.

<?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:layout_width="match_parent"
android:orientation="vertical"
android:padding="8dp"
android:layout_height="match_parent"
tools:context="com.examples.androidpractice7.MainActivity">


<com.examples.androidpractice7.CustomWidget
android:id="@+id/cswidget"
android:layout_width="match_parent"
android:layout_height="wrap_content">

</com.examples.androidpractice7.CustomWidget>

</LinearLayout>

만약 만든 CustomWidget의 Button클릭 같은 event를 다른 activity파일에서 구현해주고 싶으면 interface를 이용하면 됩니다.


아래는 interface를 이용한 예제입니다.


interface를 사용하기 위해서 추상 메소드를 만들고 setOnAMListiner 호출을 통해 다른 activity에서


추상 메소드를 구현하게  만들었습니다.


public class CustomWidget extends LinearLayout implements View.OnClickListener{
AutoCompleteTextView autoEt;
ImageView img;
Button add_btn;
Button next_btn;

int imageno = 0;

private Boolean M = false;
public CustomWidget(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}

private void init(Context context){
LayoutInflater.from(context).inflate(R.layout.customwidget,this);

autoEt = (AutoCompleteTextView)findViewById(R.id.autoCompleteTextView);
img = (ImageView)findViewById(R.id.image1);
add_btn = (Button)findViewById(R.id.b_add);
next_btn = (Button)findViewById(R.id.b_next);

add_btn.setOnClickListener(this);
next_btn.setOnClickListener(this);

}

public void setItem(Fruit temp){
autoEt.setText(temp.getName());
img.setImageResource(temp.getImgno());
add_btn.setText("M");
M = true;
}

interface OnAMListener{
void onAdd(String name, int imgno, int p);
void onModify(String name, int imgno, int p);
}

public OnAMListener onAddListener;
public OnAMListener onModifyListener;

public void setOnAMListener(OnAMListener onAddListener){
this.onAddListener = onAddListener;
this.onModifyListener = onAddListener;

}
@Override
public void onClick(View view) {
if(view == add_btn){
if(M){
onModifyListener.onModify(autoEt.getText().toString(), Fruit.image[imageno], imageno);
add_btn.setText("Add");
M = false;
autoEt.setText("");
}
else {
onAddListener.onAdd(autoEt.getText().toString(), Fruit.image[imageno], imageno);
autoEt.setText("");
}
}else{
if(imageno == Fruit.image.length -1) imageno = -1;
img.setImageResource(Fruit.image[++imageno]);
}

}
}


아래는 CustomWidget, interface을 이용해 만든 예제입니다

 


 


 




위 앱의 모든 코드는 GitHub에서 볼 수 있습니다!

https://github.com/Ywook/AndroidPractice7





안드로이드 Custom GridView 사용하기



ListView가 어댑터로부터 데이터를 받아서 리스트 형식으로 출력하는 방식이라면 GridView는 어댑터로부터 데이터를 받아서 테이블 형식으로

출력해주는 방식입니다.


 

GridView의 대표적인 속성으로 아래 3가지가 있습니다.

android:verticalSpacing=""

android:horizontalSpacing=""

android:numColumns=""


verticalSpacing은 item사이의 세로 간격을 지정하는 속성

horizontalSpacing은 item사이의 가로 간격

numColumns는 GridView의 행에 나열될 Column의 개수를 지정하는 속성입니다. 


Custom GridView를 만들기 위해서는 Custom ListView와 마찬가지로 GridView에 들어갈 item의 layout파일을 작성합니다.


그리고 BaseAdapter를 상속하는 Adapter 클래스를 만들고 Adapter와 GridView를 연결해 주면 됩니다.

public class GridViewAdapter extends BaseAdapter {
private Context context;
private ArrayList<String> data;

public GridViewAdapter(Context context, ArrayList<Fruit> data) {
this.context = context;
this.data = data;
}

@Override
public int getCount() {
return data.size();
}

@Override
public Object getItem(int i) {
return data.get(i);
}

@Override
public long getItemId(int i) {
return i;
}

@Override
public View getView(int i, View view, ViewGroup viewGroup) {

return null;

}
}


안드로이드 Custom ListView 만들기



Custom ListView는 기존 Listview처럼 문자열이나 이미지로만 표시하는게 아니라 원하는 레이아웃으로 항목에 표시하는 리스트 뷰입니다.


원하는 대로 항목을 표시하기 위해서 먼저  보여줄 Layout을 만듭니다.


그리고 만든 Layout을 항목에 보여주기 위해서 BaseAdapter를 상속받는 새로운 클래스를 정의하고 


다음 4개의 메소드를  Override해야 합니다.

public class CustomAdapter extends BaseAdapter {

@Override
public int getCount() {
return 0;
}

@Override
public Object getItem(int i) {
return null;
}

@Override
public long getItemId(int i) {
return 0;
}

@Override
public View getView(int i, View view, ViewGroup viewGroup) {
return null;
}
}


getCount() - ListView에서 사용할 데이터의 총개수

getItem(int i) - 해당 i번째의 데이터 값

getItemId(int i) - 해당 i번째 id 값

getView(int i, View view, ViewGroup viewGroup) - 이 메소드에서 화면에 보여질 아이템을 구성합니다.


getView는 스마트폰에 화면이 보여질 때 화면 구성을 위해서 호출됩니다.


즉, 데이터가 100개 들어있어도 화면에 10개의 item만 보인다면 getView를 10번 호출하고 화면에 안보이다가


다시 보이게 되는 item이 생기면 다시 getView 를 호출합니다.


ListView는 View를 재활용 하기 때문에 최초 한번만 View값이 NULL이 들어오고 그 후에 호출 되면 이미 생성된 View가 들어오게 됩니다.


따라서 아래와 같이 View가 NULL일 때만 inflate를 해주면 됩니다.

public View getView(int i, View view, ViewGroup viewGroup) {
if (view == null) {
view = LayoutInflater.from(context).inflate(R.layout.list_item, null);
}
TextView txt1 = (TextView) view.findViewById(R.id.textName);
TextView txt2 = (TextView) view.findViewById(R.id.textTel);

txt1.setText(datalist.get(i).getName());
txt2.setText(datalist.get(i).getTell());


return view;
}


아래 예제는 Custom ListView를 사용한 예제입니다.

 

 





위 예제 코드는 gitHub에서 볼 수 있습니다.

https://github.com/Ywook/Android.practice6







안드로이드 스튜디오 ListView, Intent 사용하기


ListView는 안드로이드에서 지원하는 위젯 중 하나이고 세로로 스크롤되는 목록에 항목을 표시해주는 역할을 합니다.


ListView에 나타나는 아이템들은 ArrayAdapter를 통해서 추가할 수 있습니다.


간단한 데이터를 다룰때는 ArrayAdapter 클래스를 그대로 사용하기도 하지만 사용자가 원하는 형태로 효과를 주고 싶으면


ArrayAdapter클래스를 상속 받아서 사용해야합니다.



ListView를 사용하기 위해서 해당 activity의 res/layout파일에 ListView를 추가해줍니다.




그리고 activity파일로 돌아가서 ListView를 객체화 시킵니다.


ListView에 아이템을 추가하기 위해선 Adapter를 사용해야 합니다.


즉, ListView는 Adapter를 통해 데이터를 보여주는 형식의 뷰입니다.


ListView는 .setAdapter()를 사용해서 Adapter를 설정해줄 수 있습니다.


아래에서 ArrayAdapter의 생성자 호출에 사용된 인자는 다음과 같습니다. 

  • this - context (일반적으로 Adapter를 포함하는 Activity의 instance)
  • android.R.layout.simple_list_item_1 - 어떤 resource를 넣느냐에 따라서 ListView를 구성하는 item의 text 형태나 모양                 이 바뀌는데 안드로이드에서 지원하는 것을 사용
  • data - Adapter에 적용할 data

ArrayList<String> data = new ArrayList<String>();

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,data);

Listview.setAdapter(adapter);


ListView의 아이템 클릭 이벤트는 .setOnItemClickListenr를 이용해서 설정할 수 있습니다.

listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {

}
});

그리고 ListView 아이템이 변경될 때 화면에 반영되기 위해서 adapter의 .notifyDataSetChanged()를 사용해야합니다.


adapter.notifyDataSetChanged();


안드로이드 어플리케이션을 구성하는 네 가지 요소에는 

  • Activity  - 화면을 구성하는 기본적인 컴포넌트
  • Service    - 화면 없이 백그라운드에서 동작하는 컴포넌트   
  • BroadCast Receiver - 문자메세지, 배터리 방전 등의 방송을 받는 수신자
  • Content Provider    - 응용 앱 사이에 데이터를 공유하기 위한 컴포넌트

가 있습니다.


Intent는 어플리케이션 구성 요소 간에 작업 수행을 위한 정보를 전달, 실행하는 역할을 합니다. 


예를 들면 액티비티간의 화면 전환이 있습니다.


Intent는 명시적 인텐트(Explicit Intent)암시적 인텐트(Implicit Intent)로 나눌 수 있습니다.


Intent에 클래스 객체나 컴포넌트 이름을 지정하여 호출할 대상을 알 수 있는 경우에는 명시적 인텐트


미리 정의된 액션(Action)을 지정해서 안드로이드에서 제공하는 기존 응용 프로그램을 실행하는 것을 암시적 인텐트라고 합니다.


액티비티의 호출간에 정보를 주고 받을 수 있는데 정보를 줄때는 putExtra를 사용하고 

Intent intent = new Intent(this, SecondActivity.class);

intent.putExtra("tag",et.getText().toString());

startActivity(intent);


정보를 받을때는 Get데이터타입Extra를 사용합니다.

Intent intent = getIntent();

String str = intent.getStringExtra("tag");


사용자 정의 객체도 주고 받을 수 있는데 이때는 Parcelable 타입을 사용하면 됩니다.



아래 앱은 ListView, Intent를 사용해서 만든 앱입니다.


 


 


 





전체 코드는 gitHub에서 볼 수 있습니다

https://github.com/Ywook/Android.practice5


+ Recent posts