728x90

1. Manifest에 권한설정

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

 

<Manifest 전체 소스코드>

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

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

    <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/Theme.WhoAmI">
        <activity android:name=".MainActivity"
            android:exported="true">

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

 

2. SEND_SMS 권한 확인,요청

 

public void OnCheckPermission(){

    Log.d("jinsoltest","실행1");
    if(ActivityCompat.checkSelfPermission(this, Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED
        || ActivityCompat.checkSelfPermission(this,Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED){

        if(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.SEND_SMS)){
            Toast.makeText(this, "앱 실행을 위해서는 권한을 설정해야 합니다",Toast.LENGTH_SHORT).show();

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


            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.SEND_SMS, Manifest.permission.SEND_SMS},
                    PERMISSIONS_REQUEST);
        }
    }
}
728x90
728x90

https://console.firebase.google.com/

 

로그인 - Google 계정

하나의 계정으로 모든 Google 서비스를 Google 계정으로 로그인

accounts.google.com

파이어베이스에서 데이터베이스를 만든 후 규칙을 다음과같이 허용해준다.

 

{
  "rules": {
    ".read": "true",
    ".write": "true"
  }
}

 

 

그 후 필드가 kind / name인 zoo테이블을 만든다.

 

 

 

 

그리고 animal 클래스를 만들어준다

 

//테이블이라고 생각하고, 테이블에 들어갈 속성값을 넣기
//파이어베이스는 RDBMS와 다르기 때문에 테이블이라는 개념이 없음. 원래는 키값이라고 부름
public class animal {
    String name; //동물 이름
    String kind; //동물 종류

    public animal(){} //이건 기본적으로 쓰더라구요.


    //get, set 함수는 커스텀 리스트 뷰를 사용하시는 분들과.. 필요하신 분만 작성하시면 좋습니다.
    public String getname() {
        return name;
    }

    public void setname(String name) {
        this.name = name;
    }

    public String getkind() {
        return kind;
    }

    public void setkind(String kind) {
        this.kind = kind;
    }

    //값을 추가할때 쓰는 함수, MainActivity에서 addanimal함수에서 사용할 것임.
    public animal(String name, String kind){
        this.name = name;
        this.kind = kind;
    }
}

 

 

 


데이터베이스 저장

 

 

이제 메인엑티비티에서 데이터베이스를 연동해줌 //전역변수로정의

 

// 파이어베이스 데이터베이스 연동
private final FirebaseDatabase database = FirebaseDatabase.getInstance();

//DatabaseReference는 데이터베이스의 특정 위치로 연결하는 거라고 생각하면 된다.
//현재 연결은 데이터베이스에만 딱 연결해놓고
//키값(테이블 또는 속성)의 위치 까지는 들어가지는 않은 모습이다.
private final DatabaseReference databaseReference = database.getReference();

 

public void addanimal(String name, String kind) {
    Log.d("jinsoltest","애니멀함수 ");
    //여기에서 직접 변수를 만들어서 값을 직접 넣는것도 가능합니다.
    // ex) 갓 태어난 동물만 입력해서 int age=1; 등을 넣는 경우

    //animal.java에서 선언했던 함수.
    animal animal = new animal(name,kind);

    //push()는 값을 넣을때 상위 키값을 랜덤으로 설정해 주는 함수입니다.
    //채팅기능을 만들때 사용하면 좋습니다.
    databaseReference.child("zoo").push().setValue(animal);
    //Toast.makeText(context,"db전송 :"+edit1.getText().toString()+" , "+edit2.getText().toString(),Toast.LENGTH_SHORT).show();

}

 


데이터베이스 읽기

 


public void ReadAnimal(){
    FirebaseDatabase.getInstance().getReference().addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                Log.d("jinsoltest", "ValueEventListener : " + snapshot.getValue());
            }
        }


        @Override
        public void onCancelled(@NonNull DatabaseError error) {

        }
    });

}

 

Log가 잘찍힌것을 확인할수있다.

728x90
728x90

Fragment와 Activity에서 버튼이벤트를 발생시키는것은 조금 다르다. (Fragment는 android:onClick)를 사용x )

 

프래그먼트에서는 OnClickListener를 상속받아서 구현해줘야함. onClick메소드를 오버라이드 해줘야함

 

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    Log.d("jinsoltest","oncreateview 설취");
    View view = inflater.inflate(R.layout.fragment_search,container,false);
    edit1 = (EditText) view.findViewById(R.id.edit1);
    edit2 = (EditText) view.findViewById(R.id.edit2);
    Button btn_test = (Button) view.findViewById(R.id.btn_jinsol);

    btn_test.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            addanimal(edit1.getText().toString(),edit2.getText().toString());
        }
    });
    //이부분 중요
    return view;
}

 

이런식으로 코드를 짯을때 버튼이벤트가 발생하지않는다면

 

다음과같이 상속을받아 구현해보자.

 

 

public class SlideshowFragment extends Fragment implements View.OnClickListener {

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {

        View root = inflater.inflate(R.layout.fragment_slideshow, container, false);


        Button btn123 = root.findViewById(R.id.button1);
        btn123.setOnClickListener(this);
                
    }


    @Override
    public void onClick(View v) {
        switch(v.getId())
        {
            case R.id.top_layout:
            {
                if(isAvisiable) layout1.setVisibility(View.GONE);
                else layout1.setVisibility(View.VISIBLE);
                break;
            }

        }
    }
}

 

728x90
728x90

회원가입


 

0. 반드시 파이어베이스와 앱과 연동되어있어야함

 

1. 파이어베이스에 접속하여

 

Authemtication에서 이메일/비밀번호의 상태를 반드시 사용설정됨으로 수정

 

2. 

 

전역변수로 FirebaseAuth 변수 생성

private FirebaseAuth firebaseAuth;

 

OnCreate함수 안에서 

firebaseAuth = FirebaseAuth.getInstance();

if(email.length() > 8){
                    Log.d(TAG,"등록 버튼" + email + " , " + pwd);
                    final ProgressDialog mDialog = new ProgressDialog(register.this);
                    mDialog.setMessage("가입중입니다....");
                    mDialog.show();

                    //파이어베이스에 신규계정 등록하기
                    firebaseAuth.createUserWithEmailAndPassword(email,pwd).addOnCompleteListener(register.this, new OnCompleteListener<AuthResult>() {
                        @Override
                        public void onComplete(@NonNull Task<AuthResult> task) {

                            //가입 성공시
                            if(task.isSuccessful()){
                                mDialog.dismiss();

                                FirebaseUser user = firebaseAuth.getCurrentUser();
                                String email = user.getEmail();
                                String uid = user.getUid();


                                //해쉬맵 테이블을 파이어베이스 데이터베이스에 저장
                                HashMap<Object,String> hashmap = new HashMap<>();

                                hashmap.put("uid",uid);
                                hashmap.put("email",email);

                                FirebaseDatabase database = FirebaseDatabase.getInstance();
                                DatabaseReference reference = database.getReference("Users");
                                reference.child(uid).setValue(hashmap);

                                Intent intent = new Intent(register.this, MainActivity.class);
                                startActivity(intent);
                                finish();
                                Toast.makeText(register.this,"회원가입에 성공",Toast.LENGTH_SHORT).show();

                            }else{
                                mDialog.dismiss();
                                Toast.makeText(register.this, "이미 존재하는 아이디 입니다.", Toast.LENGTH_SHORT).show();
                                return; //해당 메소드 진행을 멈추고 빠져나감
                            }
                        }
                    });

                }else{
                    Toast.makeText(register.this, "올바른 이메일 주소를 입력해주세요", Toast.LENGTH_SHORT).show();
                    return;
                }

 


 

firebaseAuth.createUserWithEmailAndPassword.(이메일,패스워드).addOnCompleteListener(activity, OnCompleteListener<AuthResult>())

 

 

addOnCompleteListener

 

FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();

UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
        .setDisplayName("Jane Q. User")
        .setPhotoUri(Uri.parse("https://example.com/jane-q-user/profile.jpg"))
        .build();

user.updateProfile(profileUpdates)
        .addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                if (task.isSuccessful()) {
                    Log.d(TAG, "User profile updated.");
                }
            }
        });

 

 

 


로그인

 

String email = mEmailText.getText().toString().trim();
String pwd = mPasswordText.getText().toString().trim();
firebaseAuth.signInWithEmailAndPassword(email,pwd).addOnCompleteListener(MainActivity.this, new OnCompleteListener<AuthResult>() {
    @Override
    public void onComplete(@NonNull Task<AuthResult> task) {
        if(task.isSuccessful()){
            Intent intent = new Intent(MainActivity.this, CopyTrade.class);
            startActivity(intent);
        }else{
            Toast.makeText(MainActivity.this,"로그인 실패",Toast.LENGTH_SHORT).show();
        }
    }
});

 

 

 

728x90
728x90

https://ititit1.tistory.com/99

 

안드로이드 앱 외부 데이터베이스 연동(Android<-> PHP <-> Maria DB) 1. 기본 설치

안드로이드 어플리케이션은 외부 DB에 바로 접근해서 데이터를 불러오지 ㅗㅅ합니다. 보안상의 이유때문에 '외부' 데이터베이스에 바로 접근은 하지못합니다. 따라서 위와같은 방식으로 애플리

ititit1.tistory.com

 

위 글에서 이어지는 내용 입니다.

 

Apache, MariaDB, PHP가 제대로 동작한다고 가정하고 진행하겠습니다.

 

우선 Apache 를 Start눌러 서버를 시작합니다.

 

그후 DB 테이블을 만들어야되는데

 

1. test 데이터베이스 접속하기

관리자 권한으로 cmd를 실행한 후 mysql test -u root -p 를 입력하여 접속합니다.

 

2. table 만들기 

create table member_test(

   userid varchar(30),

   userpassword varchar(30),

   username varchar(30)

);

 

 

2-1 테이블 구조 출력하기 

desc member_test;

 

+--------------+-------------+------+-----+---------+-------+
| Field        | Type        | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| userid       | varchar(30) | YES  |     | NULL    |       |
| usdrpassword | varchar(30) | YES  |     | NULL    |       |
| username     | varchar(30) | YES  |     | NULL    |       |
+--------------+-------------+------+-----+---------+-------+

 

3. 테이블에 필드(데이터)값 넣기

insert into member_test(userid,userpassword,username)
                      values('soll0803','dlwlsthf1','leejinsol');

 

3-1 필드 출력

select * from member_test;

 

+----------+--------------+-----------+
| userid   | userpassword | username  |
+----------+--------------+-----------+
| soll0803 | dlwlsthf1    | leejinsol |
+----------+--------------+-----------+

 

userid 열만 출력하기

MariaDB [test]> select userid from member_test;
+----------+
| userid   |
+----------+
| soll0803 |

+----------+

 

 

connect.php 파일 만들기 

 

C:\xampp\htdocs 경로에 connect.php 파일을 생성해줍니다.

 

코드는 다음과 같이 작성합니다.(저는 member_test 테이블에 값들을 4개 넣어줬습니다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<html>
<head>
    <title> :::DB TEST::</title>
    <meta charset="utf-8"/>
</head>
 
<body>
    
<?php
//1. DBMS 접속
$connect = mysqli_connect("localhost","root","비밀번호","test");
 
//2. 쿼리 실행
$query = "select * from member_test";
$result = mysqli_query($connect,$query);
$num = mysqli_num_rows($result); //행의 개수 
 
echo "rows count : ",$num;
 
 
//3. 데이터 가져오기
echo "      /     ";
 
$row = mysqli_fetch_array($result); //연관배열 형태로 들어옴
echo $row['userid'] . " " . $row['userpassword'] . " " . $row['username'];
//echo $query; 
 
echo ' / ';
$row = mysqli_fetch_array($result); //연관배열 형태로 들어옴
echo $row['userid'] . " " . $row['userpassword'] . " " . $row['username'];
 
echo ' / ';
$row = mysqli_fetch_array($result); //연관배열 형태로 들어옴
echo $row['userid'] . " " . $row['userpassword'] . " " . $row['username'];
 
echo ' / ';
$row = mysqli_fetch_array($result); //연관배열 형태로 들어옴
echo $row['userid'] . " " . $row['userpassword'] . " " . $row['username'];
 
 
 
//원래는 while문으로 돌리는게 일반적임
?>
 
</body>
</html>
cs

 

저장후 localhost/connect.php에 접속하여 확인합니다.

rows 값과

테이블의 필드값들이 성공적으로 웹에서 출력이 됩니다.

 

 

필드값들을 JSON 형태로 출력하기 

<test.php>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
    $con=mysqli_connect("localhost","root","비밀번호","test");
    mysqli_set_charset($con,"utf8");
 
    $res = mysqli_query($con,"select * from member_test");
    $result = array();
    
    while($row = mysqli_fetch_array($res)) {
        array_push($result,
            array('userid'=>$row[0],'userpassword'=>$row[1],'username'=>$row[2]));
    }
    echo json_encode(array("Tree"=>$result), JSON_UNESCAPED_UNICODE);
    mysqli_close($con);
?>
 
cs

 

위 내용들을 json viewer사이트에서 보게되면 

 

https://jsonformatter.org/json-viewer

 

Best JSON Viewer Online

Secure JSON Viewer is online JSON Viewer tool to Visualize JSON data in Tree View.

jsonformatter.org

사진대로 JSON형태로 출력이 됩니다.

728x90
728x90

안드로이드 어플리케이션은 외부 DB에 바로 접근해서 데이터를 불러오지 못합니다.

보안상의 이유때문에 '외부' 데이터베이스에 바로 접근은 하지못합니다.

 

 

따라서 위와같은 방식으로

애플리케이션 < -> PHP+아파치서버 <-> db에 통신하여 db를 가져오게된다.

즉, PHP에서 Mysql의 데이터에 접근해 echo 로 웹페이지에 mysql의 내용을 띄워 주면, 그 내용을 안드로이드에서 읽어오는 것이다. 간단히 그림으로 설명하자면 다음과 같다

 

안드로이드에서 http 요청을 통해 아파치 웹서버의 php파일이 출력하고 있는 내용을 읽어오는 것이다. 즉, 위 사진에서 

Seq : 1 Author : Yong...... 등의 내용을 가져온다. 위 웹페이지에서 띄워진 내용들은 모두 echo로(JSON 형태 ) 띄운 것이다.

 

 

먼저 실습을위해 

 

Maria DB , 아파치 , PHP 를 설치해야되는데

1. Maria DB 설치

Maria DB를 설치를 위해서 https://downloads.mariadb.org/mariadb/10.3.17/

 

MariaDB 10.3.17 Stable - MariaDB

 

downloads.mariadb.org

 

본인의 운영체제에 맞게 설치해주시면 되고 

Maria DB가 설치가 완료 되면

시작메뉴를 열어서 Heider SQL을 실행해줍니다

 

신규를 눌러서 새로운 세션을 만들어주고

세션을 우클릭하여 이름을 변경해주신후 암호를 입력하고 열기를 누르면

test1의 DB가 열리게 됩니다

 

 

 

2. XAMPP 설치

아파치와 PHP는 XAMPP 를 통해 설치해준다. 

XAMPP는 Apache, MariaDB, PHP, Perl의 약자로 설치하려는 소프트웨어를 포함하여 여러가지 기능을 제공하고 있습니다.

 

 

XAMPP를 설치하기 위해서 먼저 https://www.apachefriends.org/index.html 링크로 들어갑니다.

 

들어가셔서 본인의 운영체제에 맞게 설치해주시면 됩니다.

 

설치가 완료되면 Apache 를 Start 해줍니다.

 

 

3. 설치,동작 확인 

 

 

C:\xampp\htdocs 폴더로 가셔서 info.php 파일을 만들어주시고

 

코드를 다음과같이 작성합니다.

 

 

 

1
2
3
4
5
<?php
    $con=mysqli_connect("localhost","root","비밀번호") or die("MariaDB 접속 실패");
    phpinfo();
    mysqli_close($con);
?>
cs

이제

 

localohost/info.php 에 접속하시고

 

이런 화면이 나온다면  Apache, MariaDB, PHP가 제대로 동작된다고 보시면 됩니다.

 

참고:https://1d1cblog.tistory.com/

 

728x90
728x90

<전체 과정 요약>

 

1. implementation 'com.android.support:recyclerview-v7:28.0.0'  종속성추가

 

2. main.xml에 button을 생성해주고 버튼클릭시 activity_news.xml로 이동

 

3. activity_news.xml에는 Recyclerviewlayout컴포넌트가 배치

 

3. newsactivity.class가 실행되고 activity_news.xml에서 recyclerviewlayout 을 가져옴

 

4. newsactivity.class -> setadapter() -> activity_news.xml 과 adapter(myadapter) 매칭

 

5. adapter - oncreateviewholder 함수 & myviewholder class -  component들을 관리 

 

6. adapter생성장에 데이터를 전송할수있는 장치를 만들어주어 activity_news.xml -> adapter -> row_new.xml 데이터 전송

 

 

<activity_news.xml>

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
   
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/my_recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        </androidx.recyclerview.widget.RecyclerView>
    
    
</LinearLayout>

activity_news.xml에 id를 부여하였고 width,height 는 match로 설정함

 

 

<row_news.xml>

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <!--뉴스 이미지를 넣을 것-->
            <ImageView
                android:id="@+id/ImageView_news"
                android:layout_width="match_parent"
                android:layout_height="45dp"
                android:src="@drawable/ic_launcher_background"></ImageView>
            <!-- 뉴스의 제목을 넣을 공간 -->
            <TextView
                android:id="@+id/TextView_title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Hello, this is example of RecycleView"
                android:layout_alignBottom="@+id/ImageView_news"
                android:textSize="20dp"
                android:background="#77ff88aa"
                ></TextView>
        </RelativeLayout>
        <!--뉴스의 내용 일부를 적을 공간-->
        <TextView
            android:id="@+id/TextView_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Contents part"
            android:textSize="15dp"
            android:ellipsize="end"
            ></TextView>
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

recyclerview에 들어가는 아이템뷰 각항목의 디자인을

이미지,텍스트뷰(제목),텍스트뷰(내용)으로 설정해주었음

 

<NewsActivity.class> matched with activity_news.xml

 

package com.example.news1124;

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;


public class NewsActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager layoutManager;
    /* 리스트의 각 객체들에 들어갈 데이터들 / DB로부터 또는 사용자로부터 받아올 수 있다 */
    private String[] myDataset = {"첫 번째 뉴스의 제목 ","두 번째 뉴스의 제목","세 번째 뉴스의 제목"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        /* matching with activity_news*/
        setContentView(R.layout.activity_news);

        /* activity_news.xml에서 RecyclerView 가져옴 */
        recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        recyclerView.setHasFixedSize(true);

        // use a linear layout manager
        layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);

        /* Adapter를 연결 !
           Adapter를 사용해서 리스트의 각 항목들을 효과적으로 관리 */
        // specify an adapter (see also next example)
        mAdapter = new MyAdapter(myDataset); // Adapter생성자를 사용 해당 생성자는 Myadapter.class에서 확인
        recyclerView.setAdapter(mAdapter);

    }
}

setContentview(R.layout.activity_news) 를 통해 레이아웃을 activity_news.xml과 매칭시켜줌

 

recyclerview 를 findview를통해 연결해주고

 

recyclerview.setHasFixedSize()에 true값을 줌으로써  RecyclerView의 레이아웃 크기는 변경되지 않음

 

layoutManager = new LinearLayoutManager(this); 레이아웃매니저 생성

 

recyclerview.setLayoutmanager(layoutmanger)을통해 recyclerview에 레이아웃매니저 지정

 

madapter = new myadapter(mydataset);  

 

recyclerview.setadapter()을 통해 recyclerview에 어뎁터 지정

 

 

MyAdapter.class

 

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import android.widget.TextView;

import androidx.recyclerview.widget.RecyclerView;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    private String[] mDataset;

    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder

    /* 리스트 내의 각 항목들을 관리하는 ViewHolder class*/

    public static class MyViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public TextView TextView_title;
        public TextView TextView_content;
        public ImageView ImageView_news;

        /* 가져오는 layout에서 id값을 통해 해당 Component를 가져와 변수에 저장 */
        public MyViewHolder(View v) {
            super(v);
            TextView_title = v.findViewById(R.id.TextView_title);
            TextView_content = v.findViewById(R.id.TextView_content);
            ImageView_news = v.findViewById(R.id.ImageView_news);
        }
    }

    // Provide a suitable constructor (depends on the kind of dataset)
    /* NewsActivity에서 Adapter와 매칭시 사용하는 사용자이다
       이 때 NewsActivity의 데이터들이 데이터배열을 통해 전달되어진다
    */

    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
                                                     int viewType) {
        /* 리스트의 각 항목에 입혀줄 디자인을 생성한다 즉, create a new view
           setContentView(R.layout.xxx) 처럼 xml 매칭, 하지만 RecycleView의 특정 부분만 변경시킬 때는
           효율성을 위해 inflate함수를 사용
           사용할 xml(row_news)의 최상위 layout을 이용
         * */
        androidx.constraintlayout.widget.ConstraintLayout v
                = (androidx.constraintlayout.widget.ConstraintLayout) LayoutInflater.from(parent.getContext())
                .inflate(R.layout.row_news, parent, false);

        MyViewHolder vh = new MyViewHolder(v); /* row_news에서 components를 가져와 MyViewHolder객체의 변수들에 저장된다*/
        return vh;
    }

    // Replace the contents of a view (값 대입)
    @Override

    /* MyAdapter.class와 매칭되어있는 row_news.xml에 있는 TextView_title 컴포넌트에 값 SET */
    public void onBindViewHolder(MyViewHolder holder, int position) {
        // - get element from your dataset at this position
        // - replace the contents of the view with that element
        // TextView_title component에 값을 지정한다 NewsActivity.class에서 넘어온 mDataset에 있는 데이터
        holder.TextView_title.setText(mDataset[position]);

    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return mDataset.length;
    }
}

 

++

build.gradle(app)

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    buildToolsVersion "30.0.2"
    defaultConfig {
        applicationId "com.example.news1124"
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.3.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    implementation 'com.android.support:recyclerview-v7:28.0.0'


}

 

실시간 뉴스앱을 만들고싶다면

이부분에서 okhttp를 통해 json데이터를 잘 다뤄주면 될거같다.

이미지관리 fresco를 통해 이미지를 가져와주고

728x90
728x90

 1. 안드로이드 개발중 가장 많이 등장하는것은 리스트라 생각한다.

이미지 , 버튼보다 중요하고 더욱 많이등장하며 모든앱에 존재하고 리스트는 data를 담고있으므로 매우중요하다.

이 리스트를 구현하는방법은 여러가지가 있는데

기본적으로 ListView를 사용하는 방법이있다. 하지만 리스트뷰는 api level 1부터 존재했으며

listview의 재사용성이 떨어지며 메모리성능 부분에 많은 악영향을 미칠수있을수도 있기때문에

약간 상위버전? 업그레이드 버전으로 Recyclerview가 롤리팝(5.0)버전이 발표되엇다.

 

listview와 다른recyclerview의 재사용

개인적으로 위의 그림이 RecyclerView를 이해하는 데 도움이 많이 되었다.

그림을 이해하자면, ListView와는 다르게 RecyclerView는 이름에서 알 수 있듯이 재활용이 가능한 뷰이다. 무엇을 재활용 할까? 오른 쪽 그림을 보자. 파란색 라인 한 개가 채팅방 리스트 한 개라고 가정하자. 전체 채팅방 리스트는 100개가 훌쩍 넘을 수가 있다. 그러나 정작 화면에 보여지는 채팅방 목록은 한 번에 10개 조차 되지 않는다.

매번 사용자가 아래로 스크롤 할 때 마다 맨 위에 위치한 뷰 객체가 새로 삭제되고, 아랫 부분에서 새로 나타날 채팅방 뷰 객체를 새로 생성하면 결국 100개의 뷰 객체가 삭제되고 생성되는 것일 뿐만 아니라, 스크롤을 위아래로 왔다 갔다 하면 수 백개의 뷰 객체가 새로 생성되고 삭제됨을 반복한다.

리사이클러 뷰는 사용자가 아래로 스크롤 한다고 가정했을 때, 맨 위에 존재해서 이제 곧 사라질 뷰 객체를 삭제 하지않고 아랫쪽에서 새로 나타나날 파란색 뷰 위치로 객체를 이동시킨다. 즉 뷰 객체 자체를 재사용 하는 것인데, 중요한 점은 뷰 객체를 재사용 할 뿐이지 뷰 객체가 담고 있는 데이터(채팅방 이름)는 새로 갱신된다는 것이다. 어쨋거나 뷰 객체를 새로 생성하지는 않으므로 효율적인 것이다.

결과적으로 보자면, 맨 처음 화면에 보여질 10개 정도의 뷰 객체만을 만들고, 실제 데이터가 100개든 1000개든 원래 만들어 놓은 10개의 객체만 계속 해서 재사용 하는 것이다.

@참고  wooooooak.github.io/android/2019/03/28/recycler_view/

 

 

728x90
728x90

1. ListView

ListView에 데이터를 추가하여 화면에 표시하기 위해서는 Adapter를 사용해야한다.

 

Adappter:  "사용자가 정의한 데이터를 ListView에 출력하기 위해 사용하는 객체로, 사용자 데이터와 화면 출력 View로 이루어진 두 개의 부분을 이어주는 객체" 

 

2.ListView 기본 사용법

 

listview는 기본적인 문자열을 출력하는 listview 만으로도 리스트뷰를 구성할수있지만

우리가 쓰는 대부분의 app들의 listview의 경우 Textview, imgageview , button 등을 마음대로 배치하여 listview를 구성하고있다.

 

 

2.1ListView가 표시될 위치결정(Layout xml작성)

2.2사용자 데이터 정의(ex string 타입의 배열선언)

2.3Adapter생성후 Listview에 지정

2.4Listview클릭 이벤트처리

 

(2.1 activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity"
    >

    <ListView
        android:id="@+id/listview1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

    </ListView>

</LinearLayout>

 

 

(2.2 ~ 2.4 MainActivity.java)

package com.example.pr2;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private ListView listview1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ListView listview1 = (ListView) findViewById(R.id.listview1);
        
        //2.2 string타입의 배열선언
        String [] items = {"대한민국","중국","미국","일본"};
        
        //2.3 adapter생성
        ListAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,items);
        //2.3 adapter 을 listview에 지정
        listview1.setAdapter(adapter);

        //2.4 listview의 item클릭시 이벤트처리
        listview1.setOnItemClickListener(
                new AdapterView.OnItemClickListener(){

                    @Override
                    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                        String item = String.valueOf(parent.getItemAtPosition(position));
                        Toast.makeText(MainActivity.this,item,Toast.LENGTH_SHORT).show();
                    }
                }
        );


    }
}

 

아이템클릭시 해당 아이템의 문자열을 토스트띄우는 클릭이벤트를 넣었습니다.

728x90
728x90

* 개인 앱개발을 하면서 외부이미지를 url주소를 통해 가져와 사용하려고하는데 잘되지않아 이를 해결하고 

해결방법을 정리해보았습니다.

 

1. 네트워크 작업은 메인Thread가 아니니 별도의 Thread나 AsyncTask에서 해야한다.

2. url 이미지를 업로드하는 쓰레드가 완료될떄까지 메인쓰레드는 대기하여야한다.

3. url을 사용할것이므로 INTERNET permissin을 허용해주어야 한다. mainfest에서 

 

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:background="@color/white">


    <ImageView
        android:id="@+id/imageview1"
        android:layout_width="300dp"
        android:layout_height="300dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"></ImageView>

</androidx.constraintlayout.widget.ConstraintLayout>

 

 

MainaActivity.java

package com.example.imagecro;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.webkit.WebView;
import android.widget.ImageView;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class MainActivity extends AppCompatActivity {

    ImageView imageView;
    Bitmap bitmap;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = (ImageView)findViewById(R.id.imageview1);

        Thread mThread = new Thread() {
            @Override
            public void run() {
                try {
                    URL url = new URL("https://asddsa.soll0803.repl.co/kospi.PNG");

                    // Web에서 이미지를 가져온 뒤
                    // ImageView에 지정할 Bitmap을 만든다
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setDoInput(true); // 서버로 부터 응답 수신
                    conn.connect();

                    InputStream is = conn.getInputStream(); // InputStream 값 가져오기
                    bitmap = BitmapFactory.decodeStream(is); // Bitmap으로 변환

                } catch (MalformedURLException e) {
                    e.printStackTrace();

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };

        mThread.start(); // Thread 실행

        try {
            // 메인 Thread는 별도의 작업 Thread가 작업을 완료할 때까지 대기해야한다
            // join()를 호출하여 별도의 작업 Thread가 종료될 때까지 메인 Thread가 기다리게 한다
            mThread.join();

            // 작업 Thread에서 이미지를 불러오는 작업을 완료한 뒤
            // UI 작업을 할 수 있는 메인 Thread에서 ImageView에 이미지를 지정한다
            imageView.setImageBitmap(bitmap);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

728x90

+ Recent posts