먼저 Activity를 만들어 준다. 

package com.example.ondroid; import android.content.Intent; import android.os.Bundle; import android.widget.ImageView; import androidx.appcompat.app.AppCompatActivity; public class LoadingActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = new Intent(getApplicationContext(),MainActivity.class); startActivity(intent); finish(); } }


인텐트로 넘어가게 한 후 이 화면은 초기에만 나타나게 할 것이므로 finish()를 사용하여 Activity를 없애준다.


그리고 AndroidManifest.xml 에 아래와 같이 등록 하여준다. androi:theme="@style/SplashTheme" 를 넣었으면 style 에 SplashTheme 태크를 넣어줘야한다.


<activity android:name=".LoadingActivity" android:theme="@style/SplashTheme"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>


아래는 src/main/res/values/styles.xml 에 등록하여준다.<style> 태그이므로 기존의 태그가 서로 겹치지 않게 해야한다.


<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar"> <item name="android:windowBackground">@drawable/splash_background</item> </style>


위의 splash_background는 drawable폴더에 만들어준다.

<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/splash_base"></item> <item android:top="210dp"> <bitmap android:src="@drawable/jamanchulogi" android:gravity="top"/> </item> </layer-list>


그리고 splash_base라는 xml을 drawable 폴더에 둬서 배경색으로 이용할 수 있다.

<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <gradient android:startColor="#F4FFFF" android:centerColor="#F4FFFF" android:endColor="#F4FFFF" android:angle="90" android:centerY="0.5" /> <corners android:radius="0dp"/> </shape>



안드로이드에서 activity를 만들고 xml에 있는 text 나 버튼 등을 가져올려면 우선 그 xml파일을 activity 자바에서 불러올 필요가 있다.


public class LoadingPage extends AppCompatActivity {

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

ImageView logoImage = (ImageView) findViewById(R.id.logoImage);


}
}

위 처럼 이미지 뷰를 xml에서 생성을 하고, 이 레이아웃에 있는 이미지뷰를 엑티비티에서 읽어 들일려면 setContentView(R.layout. xml이름)를 이용하면 된다.


필자는 레이아웃을 start_loading_layout 으로 두었고 위 처럼 적었다.



그리고 activity를 생성 했다면 AndroidManifest.xml 파일에 아래 처럼 작성 해줘야 한다. <application> 태그 안에 <activity> 태그로 activity를 등록하여 주면 되고, ".엑티비티명" 을 넣어주면 된다.


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

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">

<activity android:name=".LoadingPage"/>


<activity android:name=".LoginPage"/>
</application>

</manifest>



안드로이드 에서 타이틀 바를 없애는 방법은 간단하다.

먼저 app/res/values/style.xml 로 가 준다.

<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <!--title bar 없애기--> <item name="windowActionBar">false</item> <item name="windowNoTitle">true</item> </style> </resources>

<item name="windowActionBar">false</item>

<item name="windowNoTitle">true</item>

이 두가지를 넣어주면 깔끔하게 없어 진다.

상태바는 엑티비티에서 바꿔주면 된다.

메인 엑티비티 자바코드에서 아래와 같이

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_main);

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,

WindowManager.LayoutParams.FLAG_FULLSCREEN);

이 코드를 super.onCreate()와 setContentView() 사이에 넣어준다.


매우 간단하게 아래와 같이 지정 하여 주면 된다. 이벤트 함수에서 자주 사용하게 될 것 같다.

getWindow().getDecorView().setBackgroundColor(Color.DKGRAY/*원하는 색*/);

안드로이드는 직접적으로 외부 DB를 연결할 수가 없다고 했을 때가 지금 어플을 만들면서 가장 당황하였던 부분이 아닌가 싶다.

<!-- (2019.8.5 추가) JDBC로 연결이 가능하다. -->

안드로이드는 내부에 sqlite를 이용할 때는 바로 연결할 수 있는 듯 하지만, 외부의 DB를 연결할 때는 php로 DB를 Json으로 파싱하여 웹으로 뿌려 준 후 안드로이드에서 그 Json을 받아 오는 방법으로 정보를 가져와야 한다.

구글링을 통하여 얻은 결과 가장 보편적인 방법 같은 HttpURLConnection 을 이용해볼 것이다.

//SELECT SQL문을 받아 줄 php <?php error_reporting(E_ALL); ini_set('display_errors',1); require('/*DB 커넥션 php 경로*/'); $android = strpos($_SERVER['HTTP_USER_AGENT'], "Android"); if($android) { $query=$_POST['query']; // 안드로이드에서 넘어올 query 문 $stmt = $PDO->query($query); $stmt->execute(); if ($stmt->rowCount() > 0) { $data = array(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { array_push($data, $row); } header('Content-Type: application/json; charset=utf8'); $json = json_encode(array("/*Json의 이름 되지만 지정하지 않을 수 있다.*/" => $data), JSON_PRETTY_PRINT + JSON_UNESCAPED_UNICODE); echo $json; } } ?>

이렇게 php를 작성하여 Json으로 파싱하여준다.(자바에서 쿼리문을 받아오기 때문에 여러 테이블을 참조할 수 있게 제작)

이번에는 안드로이드에서 DB의 내용을 표현할 recyclerview를 만들것이다. activity의 xml이다.

mainactivity.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:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" android:layout_margin="15dp" android:padding="10dp"> <android.support.v7.widget.RecyclerView android:id="@+id/fieldRecycle" android:layout_width="match_parent" android:layout_height="0dp" android:layout_margin="5dp" android:layout_weight="6" android:padding="5dp" /> </LinearLayout>

item_list.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:layout_margin="5dp" android:padding="5dp"> //받아올 필드가 2개라면 2개 선언! <TextView android:id="@+id/textView_field" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5dp" android:padding="5dp" /> </LinearLayout>

그리고 아래는 main activity.java이다.

import를 시켜야할 라이브러리들이다.

import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.widget.Button; import android.app.ProgressDialog; import android.os.AsyncTask; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import android.util.Log;

아래는 메인 엑티비티이다.

public class MainActivity extends AppCompatActivity { public static String IP_ADDRESS = "/*Json으로 파싱해줄 php가 있는 web 경로*/"; private static String TAG = "Json"; private RecyclerView subjectRecycle; private ArrayList<PersonalData> mArrayList; private UsersAdapter mAdapter; private String mJsonString; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 리사이클러리스트를 통하여 여러 줄로 db 표현 fieldRecycle = (RecyclerView) findViewById(R.id.fieldRecycle); fieldRecycle.setLayoutManager(new LinearLayoutManager(this)); mArrayList = new ArrayList<>(); mAdapter = new UsersAdapter(this, mArrayList); fieldRecycle.setAdapter(mAdapter); mArrayList.clear(); mAdapter.notifyDataSetChanged(); GetData task = new GetData(); task.execute( IP_ADDRESS, ""); } private class GetData extends AsyncTask<String, Void, String> { ProgressDialog progressDialog; String errorString = null; @Override protected void onPreExecute() { super.onPreExecute(); progressDialog = ProgressDialog.show(MainActivity.this, "Please Wait", null, true, true); } @Override protected void onPostExecute(String result) { super.onPostExecute(result); progressDialog.dismiss(); Log.d(TAG, "response - " + result); if (result == null){ } else { mJsonString = result; showResult(); } } @Override protected String doInBackground(String... params) { String serverURL = params[0]; String postParameters = "/* 쿼리문 작성 */"; try { URL url = new URL(serverURL); HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.setReadTimeout(5000); httpURLConnection.setConnectTimeout(5000); httpURLConnection.setRequestMethod("POST"); httpURLConnection.setDoInput(true); httpURLConnection.connect(); OutputStream outputStream = httpURLConnection.getOutputStream(); outputStream.write(postParameters.getBytes("UTF-8")); outputStream.flush(); outputStream.close(); int responseStatusCode = httpURLConnection.getResponseCode(); Log.d(TAG, "response code - " + responseStatusCode); InputStream inputStream; if(responseStatusCode == HttpURLConnection.HTTP_OK) { inputStream = httpURLConnection.getInputStream(); } else{ inputStream = httpURLConnection.getErrorStream(); } InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); StringBuilder sb = new StringBuilder(); String line; while((line = bufferedReader.readLine()) != null){ sb.append(line); } bufferedReader.close(); return sb.toString().trim(); } catch (Exception e) { Log.d(TAG, "GetData : Error ", e); errorString = e.toString(); return null; } } } private void showResult(){ String TAG_JSON="/*Json의 이름 ""로 생략 가능*/"; String TAG_field = "/*필드를 받아 올 변수*/"; try { JSONObject jsonObject = new JSONObject(mJsonString); JSONArray jsonArray = jsonObject.getJSONArray(TAG_JSON); for(int i=0;i<jsonArray.length();i++){ JSONObject item = jsonArray.getJSONObject(i); String field = item.getString(TAG_field); PersonalData personalData = new PersonalData(); personalData.setMember_subject(subject) mArrayList.add(personalData); mAdapter.notifyDataSetChanged(); } } catch (JSONException e) { Log.d(TAG, "showResult : ", e); } }

userAdaptor class 이다.

package com.example.ondroid; import android.app.Activity; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.ArrayList; public class UsersAdapter extends RecyclerView.Adapter<UsersAdapter.CustomViewHolder> { private ArrayList<PersonalData> mList = null; private Activity context = null; public UsersAdapter(Activity context, ArrayList<PersonalData> list) { this.context = context; this.mList = list; } class CustomViewHolder extends RecyclerView.ViewHolder { protected TextView subject; public CustomViewHolder(View view) { super(view); this.field = (TextView) view.findViewById(R.id.textView_field); } } @Override public CustomViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_list, null); CustomViewHolder viewHolder = new CustomViewHolder(view); return viewHolder; } @Override public void onBindViewHolder(@NonNull CustomViewHolder viewholder, int position) { viewholder.field.setText(mList.get(position).getMember_field()); } @Override public int getItemCount() { return (null != mList ? mList.size() : 0); } }

personalData class 이다.

package com.example.ondroid; public class PersonalData { private String member_field; public String getMember_field() { return member_field; } public void setMember_field(String member_field) { this.member_fieldt = member_field; } }

처음 어플을 마주하고 보니 당황스럽고 어려운 부분이 한 두가지가 아니다.

그 중 하나가 스레드를 작동시키면 다 괜찮다가 UI를 변경하는 부분이 들어가면 에러가 나면서 멈춘다는 것이다.

runOnUiThread(new Runnable() { @Override public void run() { } });

위와 같이 구현한 스레드는 runOnUiThread라는 스레드 안에 저 코드를 구현하여 UI를 변경한다면 UI가 변경되는 것을 볼 수 있을 것이다.

new Thread(new Runnable() { @Override public void run() { // 스레드 안에 삽입을 하여준다. while(true){ try { Thread.sleep(10); // 주기를 만들기 위해 만든 메소드 } catch (InterruptedException e) { e.printStackTrace(); } runOnUiThread(new Runnable() { @Override public void run() { // 이곳에 계속해서 진행할 코드를 작성 한다. } }); } } }).start();


안드로이드의 핸들러의 기존 구현 방식을 따르면 Memory 누수가 발생한다고 한다.

final Handler handle = new Handler(){ public void handleMessange(Message msg){ } };

안드로이드 스튜디오에서도 문제가 있다고 노란색 형광칠을 잔뜩 해 둔다.

이 문제의 해결법은 이렇게 핸들러를 사용하는 것이다.

final Handler handle = new Handler(new Handler(){ public boolean handleMessange(Message msg){ ... return false; } });


'Java > 안드로이드' 카테고리의 다른 글

안드로이드- php-Json -DB 통신  (0) 2019.09.12
안드로이드 runOnUiThread  (0) 2019.09.12
안드로이드 activity Life Cycle  (0) 2019.09.12
안드로이드 Timer 구현  (0) 2019.09.12
안드로이드 스튜디오 시작  (0) 2019.09.12


'Java > 안드로이드' 카테고리의 다른 글

안드로이드 runOnUiThread  (0) 2019.09.12
안드로이드 Handler 의 Memory Leak  (0) 2019.09.12
안드로이드 Timer 구현  (0) 2019.09.12
안드로이드 스튜디오 시작  (0) 2019.09.12
안드로이드 앱 개발 준비  (0) 2019.09.12

필자는 버튼을 누르면 초가 지나는 timer를 구현하고 싶다.(아래 코드는 버튼을 클릭했을 때의 이벤트 내에 있음)

그러기 위해서는 timer 스레드를 만들어 줄 필요가 있는데 코드는 다음과 같다.

final TextView Display_ = (TextView) findViewById(R.id.Display_); final Timer ssmmss = new Timer(); TimerTask outputtime = new TimerTask() { @Override public void run() { msecond ++; if(msecond > 5) { ssmmss.cancel(); }else Display_.setText(""+msecond); } } }; ssmmss.schedule(outputtime, 0,1000);

schedule 에 들어있는 것들 (timeTask 스레드, 지연시간, 주기) 이다.

이렇게 하면 필자는 바로 시간초가 나올 줄 알았지만 그렇게 간단한 것이 아니였다.

Only the original thread that created a view hierarchy can touch its views.

이런 에러가 발생을 했는데 원인은 메인 스레드 이외에 생성한 스레드로 UI를 변경할 때 나는 에러라고 한다.

그렇기에 UI를 변경하는 구문들을 빼야하는데 그러면 저 타이머 스레드를 생성한 의미가 없어진다.

고로 핸들러를 이용하여 저기 타이머 스레드가 작동할 때마다 핸들러의 내용을 불러오게 만들면 된다.

final TextView Display_ = (TextView) findViewById(R.id.Display_); final Timer ssmmss = new Timer(); final Handler timerhandler = new Handler(){ public void handleMessage(Message msg){ msecond ++; if(msecond > 5) { ssmmss.cancel(); }else Display_.setText(""+msecond); } }; TimerTask outputtime = new TimerTask() { @Override public void run() { Message msg = timerhandler.obtainMessage(); timerhandler.sendMessage(msg); } }; ssmmss.schedule(outputtime, 0,1000);

Message msg = timerhandler.obtainMessage();

timerhandler.sendMessage(msg);

이렇게 핸들러를 호출 하여주면 구동이 가능해진다.


안드로이드 스튜디오 시작화면이다. 영어를 조금만 읽어봐도 알 수 있을 정도 이다.

Start a new Android Studio project를 클릭하여 주면 위와 같은 화면이 나오게 된다.

필자는 Basic Activity를 선택하고 넘어갔다.

이름은 자동적으로 적혀있는 그대로 진행할 것이고 언어는 자바로 설정하였다.

아래의 Minimum API level에 4.0.3이라고 적혀있다. 필자의 공기계를 확인 해보니 다행이도 5버전대 였다.

최소 api 는 설정도 가능 하니 입맛에 맞게 선택할 수 있는 듯 하다.

다음을 누르니 component 설치를 진행 하였다.

이 후 프로젝트가 경로들과 코딩할 수 있는 환경이 완성 된다.

'Java > 안드로이드' 카테고리의 다른 글

안드로이드 Handler 의 Memory Leak  (0) 2019.09.12
안드로이드 activity Life Cycle  (0) 2019.09.12
안드로이드 Timer 구현  (0) 2019.09.12
안드로이드 앱 개발 준비  (0) 2019.09.12
안드로이드 앱 개발 준비(2)  (0) 2019.09.12

+ Recent posts